Пример #1
0
        /// <summary>
        /// Called when the skill dialog is started and pushed onto the dialog stack.
        /// </summary>
        /// <param name="dc">The <see cref="DialogContext"/> for the current turn of conversation.</param>
        /// <param name="options">Optional, initial information to pass to the dialog.</param>
        /// <param name="cancellationToken">A cancellation token that can be used by other objects
        /// or threads to receive notice of cancellation.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        /// <remarks>If the task is successful, the result indicates whether the dialog is still
        /// active after the turn has been processed by the dialog.</remarks>
        public override async Task <DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default)
        {
            var dialogArgs = ValidateBeginDialogArgs(options);

            // Create deep clone of the original activity to avoid altering it before forwarding it.
            var skillActivity = ObjectPath.Clone(dialogArgs.Activity);

            // Apply conversation reference and common properties from incoming activity before sending.
            skillActivity.ApplyConversationReference(dc.Context.Activity.GetConversationReference(), true);

            // Store delivery mode and connection name in dialog state for later use.
            dc.ActiveDialog.State[DeliverModeStateKey] = dialogArgs.Activity.DeliveryMode;

            // Create the conversationId and store it in the dialog context state so we can use it later
            var skillConversationId = await CreateSkillConversationIdAsync(dc.Context, dc.Context.Activity, cancellationToken).ConfigureAwait(false);

            dc.ActiveDialog.State[SkillConversationIdStateKey] = skillConversationId;

            // Send the activity to the skill.
            var eocActivity = await SendToSkillAsync(dc.Context, skillActivity, skillConversationId, cancellationToken).ConfigureAwait(false);

            if (eocActivity != null)
            {
                return(await dc.EndDialogAsync(eocActivity.Value, cancellationToken).ConfigureAwait(false));
            }

            return(EndOfTurn);
        }
Пример #2
0
        /// <summary>
        /// Called when the skill dialog is _continued_, where it is the active dialog and the
        /// user replies with a new activity.
        /// </summary>
        /// <param name="dc">The <see cref="DialogContext"/> for the current turn of conversation.</param>
        /// <param name="cancellationToken">A cancellation token that can be used by other objects
        /// or threads to receive notice of cancellation.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        /// <remarks>If the task is successful, the result indicates whether the dialog is still
        /// active after the turn has been processed by the dialog. The result may also contain a
        /// return value.</remarks>
        public override async Task <DialogTurnResult> ContinueDialogAsync(DialogContext dc, CancellationToken cancellationToken = default)
        {
            if (!OnValidateActivity(dc.Context.Activity))
            {
                return(EndOfTurn);
            }

            // Handle EndOfConversation from the skill (this will be sent to the this dialog by the SkillHandler if received from the Skill)
            if (dc.Context.Activity.Type == ActivityTypes.EndOfConversation)
            {
                return(await dc.EndDialogAsync(dc.Context.Activity.Value, cancellationToken).ConfigureAwait(false));
            }

            // Create deep clone of the original activity to avoid altering it before forwarding it.
            var skillActivity = ObjectPath.Clone(dc.Context.Activity);

            skillActivity.DeliveryMode = dc.ActiveDialog.State[DeliverModeStateKey] as string;

            var skillConversationId = (string)dc.ActiveDialog.State[SkillConversationIdStateKey];

            // Just forward to the remote skill
            var eocActivity = await SendToSkillAsync(dc.Context, skillActivity, skillConversationId, cancellationToken).ConfigureAwait(false);

            if (eocActivity != null)
            {
                return(await dc.EndDialogAsync(eocActivity.Value, cancellationToken).ConfigureAwait(false));
            }

            return(EndOfTurn);
        }
Пример #3
0
        public override async Task <DialogTurnResult> ContinueDialogAsync(DialogContext dc, CancellationToken cancellationToken = default)
        {
            await dc.Context.TraceActivityAsync($"{GetType().Name}.ContinueDialogAsync()", label : $"ActivityType: {dc.Context.Activity.Type}", cancellationToken : cancellationToken).ConfigureAwait(false);

            // Handle EndOfConversation from the skill (this will be sent to the this dialog by the SkillHandler if received from the Skill)
            if (dc.Context.Activity.Type == ActivityTypes.EndOfConversation)
            {
                await dc.Context.TraceActivityAsync($"{GetType().Name}.ContinueDialogAsync()", label : $"Got {ActivityTypes.EndOfConversation}", cancellationToken : cancellationToken).ConfigureAwait(false);

                return(await dc.EndDialogAsync(dc.Context.Activity.Value, cancellationToken).ConfigureAwait(false));
            }

            // Forward only Message and Event activities to the skill
            if (dc.Context.Activity.Type == ActivityTypes.Message || dc.Context.Activity.Type == ActivityTypes.Event)
            {
                // Create deep clone of the original activity to avoid altering it before forwarding it.
                var skillActivity = ObjectPath.Clone(dc.Context.Activity);
                skillActivity.DeliveryMode = dc.ActiveDialog.State[DeliverModeStateKey] as string;

                // Just forward to the remote skill
                var eocActivity = await SendToSkillAsync(dc.Context, skillActivity, cancellationToken).ConfigureAwait(false);

                if (eocActivity != null)
                {
                    return(await dc.EndDialogAsync(eocActivity.Value, cancellationToken).ConfigureAwait(false));
                }
            }

            return(EndOfTurn);
        }
Пример #4
0
        public override async Task <DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default)
        {
            var dialogArgs = ValidateBeginDialogArgs(options);

            await dc.Context.TraceActivityAsync($"{GetType().Name}.BeginDialogAsync()", label : $"Using activity of type: {dialogArgs.Activity.Type}", cancellationToken : cancellationToken).ConfigureAwait(false);

            // Create deep clone of the original activity to avoid altering it before forwarding it.
            var skillActivity = ObjectPath.Clone(dialogArgs.Activity);

            // Apply conversation reference and common properties from incoming activity before sending.
            skillActivity.ApplyConversationReference(dc.Context.Activity.GetConversationReference(), true);

            // Store delivery mode and connection name in dialog state for later use.
            dc.ActiveDialog.State[DeliverModeStateKey] = dialogArgs.Activity.DeliveryMode;

            // Send the activity to the skill.
            var eocActivity = await SendToSkillAsync(dc.Context, skillActivity, cancellationToken).ConfigureAwait(false);

            if (eocActivity != null)
            {
                return(await dc.EndDialogAsync(eocActivity.Value, cancellationToken).ConfigureAwait(false));
            }

            return(EndOfTurn);
        }
Пример #5
0
        public override async Task <DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default)
        {
            var dialogArgs = ValidateBeginDialogOptions(options);

            await dc.Context.TraceActivityAsync($"{GetType().Name}.BeginDialogAsync()", label : $"Using activity of type: {dialogArgs.Activity.Type}", cancellationToken : cancellationToken).ConfigureAwait(false);

            // Store Skill information for this dialog instance
            await _activeSkillProperty.SetAsync(dc.Context, dialogArgs.Skill, cancellationToken).ConfigureAwait(false);

            // Create deep clone of the original activity to avoid altering it before forwarding it.
            var skillActivity = ObjectPath.Clone(dialogArgs.Activity);

            // Apply conversation reference and common properties from incoming activity before sending.
            skillActivity.ApplyConversationReference(dc.Context.Activity.GetConversationReference(), true);

            // Send the activity to the skill.
            await SendToSkillAsync(dc, skillActivity, dialogArgs.Skill, cancellationToken).ConfigureAwait(false);

            return(EndOfTurn);
        }
Пример #6
0
        /// <summary>
        /// Called when the skill dialog is _continued_, where it is the active dialog and the
        /// user replies with a new activity.
        /// </summary>
        /// <param name="dc">The <see cref="DialogContext"/> for the current turn of conversation.</param>
        /// <param name="cancellationToken">A cancellation token that can be used by other objects
        /// or threads to receive notice of cancellation.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        /// <remarks>If the task is successful, the result indicates whether the dialog is still
        /// active after the turn has been processed by the dialog. The result may also contain a
        /// return value.</remarks>
        public override async Task <DialogTurnResult> ContinueDialogAsync(DialogContext dc, CancellationToken cancellationToken = default)
        {
            // with adaptive dialogs, ResumeDialog is not called directly.  Instead the Interrupted flag is set, which
            // acts as the signal to the SkillDialog to resume the skill.
            if (dc.State.TryGetValue <bool>(TurnPath.Interrupted, out bool interrupted) && interrupted)
            {
                // resume dialog execution
                dc.State.SetValue(TurnPath.Interrupted, false);
                return(await this.ResumeDialogAsync(dc, DialogReason.EndCalled, cancellationToken : cancellationToken).ConfigureAwait(false));
            }

            if (!OnValidateActivity(dc.Context.Activity))
            {
                return(EndOfTurn);
            }

            // Handle EndOfConversation from the skill (this will be sent to the this dialog by the SkillHandler if received from the Skill)
            if (dc.Context.Activity.Type == ActivityTypes.EndOfConversation)
            {
                return(await dc.EndDialogAsync(dc.Context.Activity.Value, cancellationToken).ConfigureAwait(false));
            }

            // Create deep clone of the original activity to avoid altering it before forwarding it.
            var skillActivity = ObjectPath.Clone(dc.Context.Activity);

            skillActivity.DeliveryMode = dc.ActiveDialog.State[DeliverModeStateKey] as string;

            var skillConversationId = (string)dc.ActiveDialog.State[SkillConversationIdStateKey];

            // Just forward to the remote skill
            var eocActivity = await SendToSkillAsync(dc.Context, skillActivity, skillConversationId, cancellationToken).ConfigureAwait(false);

            if (eocActivity != null)
            {
                return(await dc.EndDialogAsync(eocActivity.Value, cancellationToken).ConfigureAwait(false));
            }

            return(EndOfTurn);
        }
Пример #7
0
        /// <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
                });
            }
        }