Skip to content
This repository has been archived by the owner on Jun 30, 2022. It is now read-only.

Commit

Permalink
[Future to Next] Fallback skill & va change (#2076)
Browse files Browse the repository at this point in the history
* [Part 1, lib change in future branch] Add fallback event support  (#1875)

* add fallback events in lib

* update with new design

* fix warning

* fix warning

* fix issue

* fix indentation

* remove dll

* fix comments

* fix warning

* fix as comments

* fix after discussion

* fix comments

* remove extra line

* fix as comment

* fallback rule in email and VA

* add calendar support

* fix as comments

* [Lib] Fallback lib change (#1976)

* simplify lib change

* fix as comments

* fix as comments

* fix as comments

* update lib

* fix comments and upgrade to preview pkg

* fix comments

* add doc

* retry build

* add nuget feed

* fix issue

* trigger a new one
  • Loading branch information
DingmaomaoBJTU committed Aug 9, 2019
1 parent 29e8dfd commit 54b6351
Show file tree
Hide file tree
Showing 27 changed files with 557 additions and 269 deletions.
48 changes: 47 additions & 1 deletion docs/howto/skills/bestpractices.md
Original file line number Diff line number Diff line change
Expand Up @@ -425,4 +425,50 @@ For dialog state, you can save your data in `stepContext.State.Dialog[YOUR_DIALO

### Manage the dialogs

Use dialog options to transfer data among dialogs. Read [Create advanced conversation flow using branches and loops](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-dialog-manage-complex-conversation-flow?view=azure-bot-service-4.0&tabs=csharp) to learn more about dialog management.
Use dialog options to transfer data among dialogs. Read [Create advanced conversation flow using branches and loops](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-dialog-manage-complex-conversation-flow?view=azure-bot-service-4.0&tabs=csharp) to learn more about dialog management.

## Support skill fallback
Currently if you want to support skill switching scenarios like this in Virtual Assistant:

- User: What's my meetings today?

- Bot (Virtual Assistant, Calendar skill): [Meetings], do you want to hear the first one?

- User: What tasks do I have?

- Bot (Virtual Assistant): Are you sure to switch to todoSkill?

- User: Yes, please.

- Bot (Virtual Assistant, To Do skill): [To do list].

You can make this happen by sending the FallbackEvent back to Virtual Assistant, to confirm whether other skills are able to handle this utterance.

```csharp
protected async Task<DialogTurnResult> SendFallback(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken))
{
try
{
var state = await EmailStateAccessor.GetAsync(sc.Context);

// Send Fallback Event
if (sc.Context.Adapter is EmailSkillWebSocketBotAdapter remoteInvocationAdapter)
{
await remoteInvocationAdapter.SendRemoteFallbackEventAsync(sc.Context, cancellationToken).ConfigureAwait(false);

// Wait for the FallbackHandle event
return await sc.PromptAsync(Actions.FallbackEventPrompt, new PromptOptions()).ConfigureAwait(false);
}

return await sc.NextAsync();
}
catch (Exception ex)
{
await HandleDialogExceptions(sc, ex);

return new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs);
}
}
```

If other skills can handle it, Virtual Assistant will cancel current skill and pass user input to the proper skill. If not, Virtual Assistant will send back a FallbackHandledEvent to continue current skill.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
<TargetFramework>netcoreapp2.2</TargetFramework>
<UserSecretsId>2bdd896c-393e-47c4-b6b9-a47238550b83</UserSecretsId>
<NoWarn>NU1701</NoWarn>
<RestoreAdditionalProjectSources>
https://botbuilder.myget.org/F/aitemplates/api/v3/index.json;
</RestoreAdditionalProjectSources>
</PropertyGroup>

<ItemGroup>
Expand Down Expand Up @@ -104,8 +107,8 @@
<PackageReference Include="Microsoft.Bot.Builder.Dialogs" Version="4.5.1" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.ApplicationInsights.Core" Version="4.5.1" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Version="4.5.1" />
<PackageReference Include="Microsoft.Bot.Builder.Skills" Version="4.5.3" />
<PackageReference Include="Microsoft.Bot.Builder.Solutions" Version="4.5.3" />
<PackageReference Include="Microsoft.Bot.Builder.Skills" Version="4.6.0-daily6" />
<PackageReference Include="Microsoft.Bot.Builder.Solutions" Version="4.6.0-daily6" />
<PackageReference Include="Microsoft.Bot.Builder.TemplateManager" Version="4.5.1" />
<PackageReference Include="Microsoft.Bot.Configuration" Version="4.5.1" />
<PackageReference Include="Microsoft.Bot.Connector" Version="4.5.1" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ private async Task PopulateStateFromSemanticAction(ITurnContext context)
await dc.Context.SendActivityAsync(response);
}

// End active dialog
// End active dialog.
await dc.EndDialogAsync(result);
}

Expand All @@ -224,6 +224,7 @@ private async Task PopulateStateFromSemanticAction(ITurnContext context)
switch (dc.Context.Activity.Name)
{
case TokenEvents.TokenResponseEventName:
case SkillEvents.FallbackHandledEventName:
{
// Auth dialog completion
var result = await dc.ContinueDialogAsync();
Expand Down
145 changes: 104 additions & 41 deletions skills/src/csharp/calendarskill/calendarskill/Dialogs/SummaryDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CalendarSkill.Adapters;
using CalendarSkill.Models;
using CalendarSkill.Responses.Shared;
using CalendarSkill.Responses.Summary;
Expand All @@ -13,6 +14,8 @@
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Builder.Skills.Models;
using Microsoft.Bot.Builder.Solutions;
using Microsoft.Bot.Builder.Solutions.Resources;
using Microsoft.Bot.Builder.Solutions.Responses;
using Microsoft.Bot.Builder.Solutions.Util;
Expand Down Expand Up @@ -69,13 +72,22 @@ public SummaryDialog(
AfterReadOutEvent,
};

var retryUnknown = new WaterfallStep[]
{
SendFallback,
RetryInput,
HandleActions,
};

// Define the conversation flow using a waterfall model.
AddDialog(new WaterfallDialog(Actions.GetEventsInit, initStep) { TelemetryClient = telemetryClient });
AddDialog(new WaterfallDialog(Actions.ShowNextEvent, showNext) { TelemetryClient = telemetryClient });
AddDialog(new WaterfallDialog(Actions.ShowEventsSummary, showSummary) { TelemetryClient = telemetryClient });
AddDialog(new WaterfallDialog(Actions.Read, readEvent) { TelemetryClient = telemetryClient });
AddDialog(updateEventDialog ?? throw new ArgumentNullException(nameof(updateEventDialog)));
AddDialog(changeEventStatusDialog ?? throw new ArgumentNullException(nameof(changeEventStatusDialog)));
AddDialog(new WaterfallDialog(Actions.RetryUnknown, retryUnknown) { TelemetryClient = telemetryClient });
AddDialog(new EventPrompt(Actions.FallbackEventPrompt, SkillEvents.FallbackHandledEventName, ResponseValidatorAsync));

// Set starting dialog for component
InitialDialogId = Actions.GetEventsInit;
Expand Down Expand Up @@ -590,58 +602,57 @@ await sc.Context.SendActivityAsync(await GetOverviewMeetingListResponseAsync(
if (state.FocusEvents.Count <= 0)
{
var currentList = GetCurrentPageMeetings(state.SummaryEvents, state);
if (state.UserSelectIndex < currentList.Count)
if (state.UserSelectIndex >= 0 && state.UserSelectIndex < currentList.Count)
{
state.FocusEvents.Add(currentList[state.UserSelectIndex]);
}
else
{
return await sc.EndDialogAsync();
}
}

var focusEvent = state.FocusEvents.First();
if (focusEvent.IsOrganizer)
if (state.FocusEvents != null && state.FocusEvents.Count > 0)
{
if (topIntent == CalendarLuis.Intent.ChangeCalendarEntry)
var focusEvent = state.FocusEvents.First();
if (focusEvent != null)
{
state.Events.Add(focusEvent);
state.IsActionFromSummary = true;
return await sc.BeginDialogAsync(nameof(UpdateEventDialog), sc.Options);
}
if (focusEvent.IsOrganizer)
{
if (topIntent == CalendarLuis.Intent.ChangeCalendarEntry)
{
state.Events.Add(focusEvent);
state.IsActionFromSummary = true;
return await sc.BeginDialogAsync(nameof(UpdateEventDialog), sc.Options);
}

if (topIntent == CalendarLuis.Intent.DeleteCalendarEntry)
{
state.Events.Add(focusEvent);
state.IsActionFromSummary = true;
return await sc.BeginDialogAsync(nameof(ChangeEventStatusDialog), sc.Options);
}
}
else if (focusEvent.IsAccepted)
{
if (topIntent == CalendarLuis.Intent.DeleteCalendarEntry)
{
state.Events.Add(focusEvent);
state.IsActionFromSummary = true;
return await sc.BeginDialogAsync(nameof(ChangeEventStatusDialog), sc.Options);
}
}
else
{
if (topIntent == CalendarLuis.Intent.DeleteCalendarEntry || topIntent == CalendarLuis.Intent.AcceptEventEntry)
{
state.Events.Add(focusEvent);
state.IsActionFromSummary = true;
return await sc.BeginDialogAsync(nameof(ChangeEventStatusDialog), sc.Options);
}
}
if (topIntent == CalendarLuis.Intent.DeleteCalendarEntry)
{
state.Events.Add(focusEvent);
state.IsActionFromSummary = true;
return await sc.BeginDialogAsync(nameof(ChangeEventStatusDialog), sc.Options);
}
}
else if (focusEvent.IsAccepted)
{
if (topIntent == CalendarLuis.Intent.DeleteCalendarEntry)
{
state.Events.Add(focusEvent);
state.IsActionFromSummary = true;
return await sc.BeginDialogAsync(nameof(ChangeEventStatusDialog), sc.Options);
}
}
else
{
if (topIntent == CalendarLuis.Intent.DeleteCalendarEntry || topIntent == CalendarLuis.Intent.AcceptEventEntry)
{
state.Events.Add(focusEvent);
state.IsActionFromSummary = true;
return await sc.BeginDialogAsync(nameof(ChangeEventStatusDialog), sc.Options);
}
}

if (state.FocusEvents != null)
{
return await sc.BeginDialogAsync(Actions.Read, sc.Options);
return await sc.BeginDialogAsync(Actions.Read, sc.Options);
}
}

return await sc.NextAsync();
return await sc.ReplaceDialogAsync(Actions.RetryUnknown, sc.Options);
}
catch (Exception ex)
{
Expand Down Expand Up @@ -831,5 +842,57 @@ private bool SearchesTodayMeeting(CalendarSkillState state)
!state.EndTime.Any() &&
EventModel.IsSameDate(searchDate, userNow);
}

protected Task<bool> ResponseValidatorAsync(PromptValidatorContext<Activity> pc, CancellationToken cancellationToken)
{
var activity = pc.Recognized.Value;
if (activity != null && activity.Type == ActivityTypes.Event && activity.Name == SkillEvents.FallbackHandledEventName)
{
return Task.FromResult(true);
}

return Task.FromResult(false);
}

protected async Task<DialogTurnResult> SendFallback(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken))
{
try
{
var state = await Accessor.GetAsync(sc.Context);

// Send Fallback Event
if (sc.Context.Adapter is CalendarSkillWebSocketBotAdapter remoteInvocationAdapter)
{
await remoteInvocationAdapter.SendRemoteFallbackEventAsync(sc.Context, cancellationToken).ConfigureAwait(false);

// Wait for the FallbackHandle event
return await sc.PromptAsync(Actions.FallbackEventPrompt, new PromptOptions()).ConfigureAwait(false);
}

return await sc.NextAsync();
}
catch (Exception ex)
{
await HandleDialogExceptions(sc, ex);

return new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs);
}
}

protected async Task<DialogTurnResult> RetryInput(WaterfallStepContext sc, CancellationToken cancellationToken = default(CancellationToken))
{
try
{
var state = await Accessor.GetAsync(sc.Context);

return await sc.PromptAsync(Actions.Prompt, new PromptOptions { Prompt = ResponseManager.GetResponse(CalendarSharedResponses.RetryInput) });
}
catch (Exception ex)
{
await HandleDialogExceptions(sc, ex);

return new DialogTurnResult(DialogTurnStatus.Cancelled, CommonUtil.DialogTurnResultCancelAllDialogs);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ public class CalendarSharedResponses : IResponseIdCollection
public const string ActionEnded = "ActionEnded";
public const string CalendarErrorMessage = "CalendarErrorMessage";
public const string CalendarErrorMessageBotProblem = "CalendarErrorMessageBotProblem";
public const string RetryInput = "RetryInput";
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading

0 comments on commit 54b6351

Please sign in to comment.