protected virtual Task <DialogTurnResult> OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken = default(CancellationToken)) { return(innerDc.ContinueDialogAsync(cancellationToken)); }
/// <summary> /// Runs dialog system in the context of an ITurnContext. /// </summary> /// <param name="context">turn context.</param> /// <param name="cancellationToken">cancelation token.</param> /// <returns>result of the running the logic against the activity.</returns> public async Task <DialogManagerResult> OnTurnAsync(ITurnContext context, CancellationToken cancellationToken = default(CancellationToken)) { var botStateSet = new BotStateSet(); // preload turnstate with DM turnstate foreach (var pair in this.TurnState) { context.TurnState.Set(pair.Key, pair.Value); } if (this.ConversationState == null) { this.ConversationState = context.TurnState.Get <ConversationState>() ?? throw new ArgumentNullException(nameof(this.ConversationState)); } else { context.TurnState.Set(this.ConversationState); } botStateSet.Add(this.ConversationState); if (this.UserState == null) { this.UserState = context.TurnState.Get <UserState>(); } if (this.UserState != null) { botStateSet.Add(this.UserState); } // create property accessors var lastAccessProperty = ConversationState.CreateProperty <DateTime>(LASTACCESS); var lastAccess = await lastAccessProperty.GetAsync(context, () => DateTime.UtcNow, cancellationToken : cancellationToken).ConfigureAwait(false); // Check for expired conversation var now = DateTime.UtcNow; if (this.ExpireAfter.HasValue && (DateTime.UtcNow - lastAccess) >= TimeSpan.FromMilliseconds((double)this.ExpireAfter)) { // Clear conversation state await ConversationState.ClearStateAsync(context, cancellationToken : cancellationToken).ConfigureAwait(false); } lastAccess = DateTime.UtcNow; await lastAccessProperty.SetAsync(context, lastAccess, cancellationToken : cancellationToken).ConfigureAwait(false); // get dialog stack var dialogsProperty = ConversationState.CreateProperty <DialogState>(this.dialogStateProperty); DialogState dialogState = await dialogsProperty.GetAsync(context, () => new DialogState(), cancellationToken : cancellationToken).ConfigureAwait(false); // Create DialogContext var dc = new DialogContext(this.Dialogs, context, dialogState); // get the dialogstatemanager configuration var dialogStateManager = new DialogStateManager(dc, this.StateConfiguration); await dialogStateManager.LoadAllScopesAsync(cancellationToken).ConfigureAwait(false); dc.Context.TurnState.Add(dialogStateManager); DialogTurnResult turnResult = null; // Loop as long as we are getting valid OnError handled we should continue executing the actions for the turn. // // NOTE: We loop around this block because each pass through we either complete the turn and break out of the loop // or we have had an exception AND there was an OnError action which captured the error. We need to continue the // turn based on the actions the OnError handler introduced. while (true) { try { if (dc.ActiveDialog == null) { // start root dialog turnResult = await dc.BeginDialogAsync(this.rootDialogId, cancellationToken : cancellationToken).ConfigureAwait(false); } else { // Continue execution // - This will apply any queued up interruptions and execute the current/next step(s). turnResult = await dc.ContinueDialogAsync(cancellationToken : cancellationToken).ConfigureAwait(false); if (turnResult.Status == DialogTurnStatus.Empty) { // restart root dialog turnResult = await dc.BeginDialogAsync(this.rootDialogId, cancellationToken : cancellationToken).ConfigureAwait(false); } } // turn successfully completed, break the loop break; } catch (Exception err) { // fire error event, bubbling from the leaf. var handled = await dc.EmitEventAsync(DialogEvents.Error, err, bubble : true, fromLeaf : true, cancellationToken : cancellationToken).ConfigureAwait(false); if (!handled) { // error was NOT handled, throw the exception and end the turn. (This will trigger the Adapter.OnError handler and end the entire dialog stack) throw; } } } // save all state scopes to their respective botState locations. await dialogStateManager.SaveAllChangesAsync(cancellationToken).ConfigureAwait(false); // save botstate changes await botStateSet.SaveAllChangesAsync(dc.Context, false, cancellationToken).ConfigureAwait(false); // send trace of memory var snapshot = dc.GetState().GetMemorySnapshot(); var traceActivity = (Activity)Activity.CreateTraceActivity("BotState", "https://www.botframework.com/schemas/botState", snapshot, "Bot State"); await dc.Context.SendActivityAsync(traceActivity).ConfigureAwait(false); return(new DialogManagerResult() { TurnResult = turnResult }); }
/// <summary> /// Runs dialog system in the context of an ITurnContext. /// </summary> /// <param name="context">turn context.</param> /// <param name="cancellationToken">cancelation token.</param> /// <returns>result of the running the logic against the activity.</returns> public async Task <DialogManagerResult> OnTurnAsync(ITurnContext context, CancellationToken cancellationToken = default(CancellationToken)) { BotStateSet botStateSet = new BotStateSet(); ConversationState conversationState = this.ConversationState ?? context.TurnState.Get <ConversationState>() ?? throw new ArgumentNullException($"{nameof(ConversationState)} is not found in the turn context. Have you called adapter.UseState() with a configured ConversationState object?"); UserState userState = this.UserState ?? context.TurnState.Get <UserState>(); if (conversationState != null) { botStateSet.Add(conversationState); } if (userState != null) { botStateSet.Add(userState); } // create property accessors var lastAccessProperty = conversationState.CreateProperty <DateTime>(LASTACCESS); var lastAccess = await lastAccessProperty.GetAsync(context, () => DateTime.UtcNow, cancellationToken : cancellationToken).ConfigureAwait(false); // Check for expired conversation var now = DateTime.UtcNow; if (this.ExpireAfter.HasValue && (DateTime.UtcNow - lastAccess) >= TimeSpan.FromMilliseconds((double)this.ExpireAfter)) { // Clear conversation state await conversationState.ClearStateAsync(context, cancellationToken : cancellationToken).ConfigureAwait(false); } lastAccess = DateTime.UtcNow; await lastAccessProperty.SetAsync(context, lastAccess, cancellationToken : cancellationToken).ConfigureAwait(false); // get dialog stack var dialogsProperty = conversationState.CreateProperty <DialogState>(DIALOGS); DialogState dialogState = await dialogsProperty.GetAsync(context, () => new DialogState(), cancellationToken : cancellationToken).ConfigureAwait(false); // Create DialogContext var dc = new DialogContext(this.dialogSet, context, dialogState); // set DSM configuration dc.SetStateConfiguration(this.StateConfiguration ?? DialogStateManager.CreateStandardConfiguration(conversationState, userState)); // load scopes await dc.GetState().LoadAllScopesAsync(cancellationToken).ConfigureAwait(false); DialogTurnResult turnResult = null; if (dc.ActiveDialog == null) { // start root dialog turnResult = await dc.BeginDialogAsync(this.rootDialogId, cancellationToken : cancellationToken).ConfigureAwait(false); } else { // Continue execution // - This will apply any queued up interruptions and execute the current/next step(s). turnResult = await dc.ContinueDialogAsync(cancellationToken : cancellationToken).ConfigureAwait(false); if (turnResult.Status == DialogTurnStatus.Empty) { // restart root dialog turnResult = await dc.BeginDialogAsync(this.rootDialogId, cancellationToken : cancellationToken).ConfigureAwait(false); } } // save all state scopes to their respective stores. await dc.GetState().SaveAllChangesAsync(cancellationToken).ConfigureAwait(false); // save botstate changes await botStateSet.SaveAllChangesAsync(dc.Context, false, cancellationToken).ConfigureAwait(false); // send trace of memory var snapshot = dc.GetState().GetMemorySnapshot(); var traceActivity = (Activity)Activity.CreateTraceActivity("BotState", "https://www.botframework.com/schemas/botState", snapshot, "Bot State"); await dc.Context.SendActivityAsync(traceActivity).ConfigureAwait(false); return(new DialogManagerResult() { TurnResult = turnResult }); }
private static async Task <DialogTurnResult> InnerRunAsync(ITurnContext turnContext, string dialogId, DialogContext dialogContext, CancellationToken cancellationToken) { // Handle EoC and Reprompt event from a parent bot (can be root bot to skill or skill to skill) if (IsFromParentToSkill(turnContext)) { // Handle remote cancellation request from parent. if (turnContext.Activity.Type == ActivityTypes.EndOfConversation) { if (!dialogContext.Stack.Any()) { // No dialogs to cancel, just return. return(new DialogTurnResult(DialogTurnStatus.Empty)); } var activeDialogContext = GetActiveDialogContext(dialogContext); // Send cancellation message to the top dialog in the stack to ensure all the parents are canceled in the right order. return(await activeDialogContext.CancelAllDialogsAsync(true, cancellationToken : cancellationToken).ConfigureAwait(false)); } // Handle a reprompt event sent from the parent. if (turnContext.Activity.Type == ActivityTypes.Event && turnContext.Activity.Name == DialogEvents.RepromptDialog) { if (!dialogContext.Stack.Any()) { // No dialogs to reprompt, just return. return(new DialogTurnResult(DialogTurnStatus.Empty)); } await dialogContext.RepromptDialogAsync(cancellationToken).ConfigureAwait(false); return(new DialogTurnResult(DialogTurnStatus.Waiting)); } } // Continue or start the dialog. var result = await dialogContext.ContinueDialogAsync(cancellationToken).ConfigureAwait(false); if (result.Status == DialogTurnStatus.Empty) { result = await dialogContext.BeginDialogAsync(dialogId, null, cancellationToken).ConfigureAwait(false); } await SendStateSnapshotTraceAsync(dialogContext, cancellationToken).ConfigureAwait(false); // Skills should send EoC when the dialog completes. if (result.Status == DialogTurnStatus.Complete || result.Status == DialogTurnStatus.Cancelled) { if (SendEoCToParent(turnContext)) { // Send End of conversation at the end. var code = result.Status == DialogTurnStatus.Complete ? EndOfConversationCodes.CompletedSuccessfully : EndOfConversationCodes.UserCancelled; var activity = new Activity(ActivityTypes.EndOfConversation) { Value = result.Result, Locale = turnContext.Activity.Locale, Code = code }; await turnContext.SendActivityAsync(activity, cancellationToken).ConfigureAwait(false); } } return(result); }
/// <summary> /// Runs dialog system in the context of an ITurnContext. /// </summary> /// <param name="context">turn context.</param> /// <param name="state">stored state.</param> /// <param name="cancellationToken">cancelation token.</param> /// <returns>result of the running the logic against the activity.</returns> public async Task <DialogManagerResult> OnTurnAsync(ITurnContext context, PersistedState state = null, CancellationToken cancellationToken = default(CancellationToken)) { var saveState = false; var keys = GetKeys(context); var storage = context.TurnState.Get <IStorage>(); if (state == null) { if (storage == null) { throw new Exception("DialogManager: unable to load the bots state.Bot.storage not assigned."); } state = await LoadStateAsync(storage, keys).ConfigureAwait(false); saveState = true; } // Clone state to preserve original state var newState = ObjectPath.Clone(state); // Check for expired conversation var now = DateTime.UtcNow; if (this.ExpireAfter.HasValue && newState.ConversationState.ContainsKey(LASTACCESS)) { var lastAccess = DateTime.Parse(newState.ConversationState[LASTACCESS] as string); if ((DateTime.UtcNow - lastAccess) >= TimeSpan.FromMilliseconds((double)this.ExpireAfter)) { // Clear conversation state state.ConversationState = new Dictionary <string, object>(); state.ConversationState[ETAG] = newState.ConversationState[ETAG]; } } newState.ConversationState[LASTACCESS] = DateTime.UtcNow.ToString("u"); // Ensure dialog stack populated DialogState dialogState; if (!newState.ConversationState.ContainsKey(DIALOGS)) { dialogState = new DialogState(); newState.ConversationState[DIALOGS] = dialogState; } else { dialogState = (DialogState)newState.ConversationState[DIALOGS]; } var namedScopes = MemoryScope.GetScopesMemory(context); namedScopes[ScopePath.USER] = newState.UserState; namedScopes[ScopePath.CONVERSATION] = newState.ConversationState; namedScopes[ScopePath.TURN] = new Dictionary <string, object>(StringComparer.InvariantCultureIgnoreCase); // Create DialogContext var dc = new DialogContext( this.dialogSet, context, dialogState); DialogTurnResult turnResult = null; if (dc.ActiveDialog == null) { // start root dialog turnResult = await dc.BeginDialogAsync(this.rootDialogId, cancellationToken : cancellationToken).ConfigureAwait(false); } else { // Continue execution // - This will apply any queued up interruptions and execute the current/next step(s). turnResult = await dc.ContinueDialogAsync(cancellationToken : cancellationToken).ConfigureAwait(false); if (turnResult.Status == DialogTurnStatus.Empty) { // restart root dialog turnResult = await dc.BeginDialogAsync(this.rootDialogId, cancellationToken : cancellationToken).ConfigureAwait(false); } } // send trace of memory await dc.Context.SendActivityAsync((Activity)Activity.CreateTraceActivity("BotState", "https://www.botframework.com/schemas/botState", dc.State.GetMemorySnapshot(), "Bot State")).ConfigureAwait(false); // Save state if loaded from storage if (saveState) { await DialogManager.SaveStateAsync(storage, keys : keys, newState : newState, oldState : state, eTag : "*").ConfigureAwait(false); return(new DialogManagerResult() { TurnResult = turnResult }); } else { return(new DialogManagerResult() { TurnResult = turnResult, NewState = newState }); } }
/// <summary> /// Runs dialog system in the context of an ITurnContext. /// </summary> /// <param name="context">turn context.</param> /// <param name="cancellationToken">cancelation token.</param> /// <returns>result of the running the logic against the activity.</returns> public async Task <DialogManagerResult> OnTurnAsync(ITurnContext context, CancellationToken cancellationToken = default(CancellationToken)) { ConversationState conversationState = context.TurnState.Get <ConversationState>() ?? throw new ArgumentNullException($"{nameof(ConversationState)} is not found in the turn context. Have you called adapter.UseState() with a configured ConversationState object?"); UserState userState = context.TurnState.Get <UserState>() ?? throw new ArgumentNullException($"{nameof(UserState)} is not found in the turn context. Have you called adapter.UseState() with a configured UserState object?"); // create property accessors var lastAccessProperty = conversationState.CreateProperty <DateTime>(LASTACCESS); var dialogsProperty = conversationState.CreateProperty <DialogState>(DIALOGS); var userScope = userState.CreateProperty <object>($"{ScopePath.USER}{nameof(MemoryScope)}"); var conversationScope = conversationState.CreateProperty <object>($"{ScopePath.CONVERSATION}{nameof(MemoryScope)}"); var lastAccess = await lastAccessProperty.GetAsync(context, () => DateTime.UtcNow, cancellationToken : cancellationToken).ConfigureAwait(false); // Check for expired conversation var now = DateTime.UtcNow; if (this.ExpireAfter.HasValue && (DateTime.UtcNow - lastAccess) >= TimeSpan.FromMilliseconds((double)this.ExpireAfter)) { // Clear conversation state await conversationState.ClearStateAsync(context, cancellationToken : cancellationToken).ConfigureAwait(false); } lastAccess = DateTime.UtcNow; await lastAccessProperty.SetAsync(context, lastAccess, cancellationToken : cancellationToken).ConfigureAwait(false); // get dialog stack DialogState dialogState = await dialogsProperty.GetAsync(context, () => new DialogState(), cancellationToken : cancellationToken).ConfigureAwait(false); var namedScopes = MemoryScope.GetScopesMemory(context); namedScopes[ScopePath.USER] = await userScope.GetAsync(context, () => new Dictionary <string, object>(StringComparer.InvariantCultureIgnoreCase), cancellationToken : cancellationToken).ConfigureAwait(false); namedScopes[ScopePath.CONVERSATION] = await conversationScope.GetAsync(context, () => new Dictionary <string, object>(StringComparer.InvariantCultureIgnoreCase), cancellationToken : cancellationToken).ConfigureAwait(false); namedScopes[ScopePath.TURN] = new Dictionary <string, object>(StringComparer.InvariantCultureIgnoreCase); // Create DialogContext var dc = new DialogContext(this.dialogSet, context, dialogState); DialogTurnResult turnResult = null; if (dc.ActiveDialog == null) { // start root dialog turnResult = await dc.BeginDialogAsync(this.rootDialogId, cancellationToken : cancellationToken).ConfigureAwait(false); } else { // Continue execution // - This will apply any queued up interruptions and execute the current/next step(s). turnResult = await dc.ContinueDialogAsync(cancellationToken : cancellationToken).ConfigureAwait(false); if (turnResult.Status == DialogTurnStatus.Empty) { // restart root dialog turnResult = await dc.BeginDialogAsync(this.rootDialogId, cancellationToken : cancellationToken).ConfigureAwait(false); } } // send trace of memory await dc.Context.SendActivityAsync((Activity)Activity.CreateTraceActivity("BotState", "https://www.botframework.com/schemas/botState", dc.State.GetMemorySnapshot(), "Bot State")).ConfigureAwait(false); return(new DialogManagerResult() { TurnResult = turnResult }); }