Esempio n. 1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SkillDialog"/> class.
        /// SkillDialog constructor that accepts the manifest description of a Skill along with TelemetryClient for end to end telemetry.
        /// </summary>
        /// <param name="skillManifest">Skill manifest.</param>
        /// <param name="serviceClientCredentials">Service client credentials.</param>
        /// <param name="telemetryClient">Telemetry Client.</param>
        /// <param name="userState">User State.</param>
        /// <param name="authDialog">Auth Dialog.</param>
        /// <param name="skillIntentRecognizer">Skill Intent Recognizer.</param>
        /// <param name="skillTransport">Transport used for skill invocation.</param>
        public SkillDialog(
            SkillManifest skillManifest,
            IServiceClientCredentials serviceClientCredentials,
            IBotTelemetryClient telemetryClient,
            UserState userState,
            MultiProviderAuthDialog authDialog           = null,
            ISkillIntentRecognizer skillIntentRecognizer = null,
            ISkillTransport skillTransport = null)
            : base(skillManifest.Id)
        {
            _skillManifest            = skillManifest ?? throw new ArgumentNullException(nameof(SkillManifest));
            _serviceClientCredentials = serviceClientCredentials ?? throw new ArgumentNullException(nameof(serviceClientCredentials));
            _userState             = userState;
            _skillTransport        = skillTransport ?? new SkillWebSocketTransport(telemetryClient);
            _skillIntentRecognizer = skillIntentRecognizer;

            var intentSwitching = new WaterfallStep[]
            {
                ConfirmIntentSwitch,
                FinishIntentSwitch,
            };

            if (authDialog != null)
            {
                _authDialog = authDialog;
                AddDialog(authDialog);
            }

            AddDialog(new WaterfallDialog(DialogIds.ConfirmSkillSwitchFlow, intentSwitching));
            AddDialog(new ConfirmPrompt(DialogIds.ConfirmSkillSwitchPrompt));
        }
        public static SkillManifest CreateSkill(string id, string name, string endpoint, string actionId, List <Slot> slots = null)
        {
            var skillManifest = new SkillManifest
            {
                Name     = name,
                Id       = id,
                Endpoint = new Uri(endpoint),
                Actions  = new List <Solutions.Skills.Models.Manifest.Action>(),
            };

            var action = new Solutions.Skills.Models.Manifest.Action
            {
                Id         = actionId,
                Definition = new ActionDefinition(),
            };

            // Provide slots if we have them
            if (slots != null)
            {
                action.Definition.Slots = slots;
            }

            skillManifest.Actions.Add(action);

            return(skillManifest);
        }
        public async Task <Activity> ForwardToSkillAsync(SkillManifest skillManifest, IServiceClientCredentials serviceClientCredentials, ITurnContext turnContext, Activity activity, Action <Activity> tokenRequestHandler = null, Action <Activity> fallbackHandler = null)
        {
            if (_streamingTransportClient == null)
            {
                // establish websocket connection
                _streamingTransportClient = new WebSocketClient(
                    EnsureWebSocketUrl(skillManifest.Endpoint.ToString()),
                    new SkillCallingRequestHandler(
                        turnContext,
                        _botTelemetryClient,
                        GetTokenCallback(turnContext, tokenRequestHandler),
                        GetFallbackCallback(turnContext, fallbackHandler),
                        GetHandoffActivityCallback()));
            }

            // acquire AAD token
            MicrosoftAppCredentials.TrustServiceUrl(skillManifest.Endpoint.AbsoluteUri);
            var token = await serviceClientCredentials.GetTokenAsync();

            // put AAD token in the header
            var headers = new Dictionary <string, string>();

            headers.Add("Authorization", $"Bearer {token}");

            await _streamingTransportClient.ConnectAsync(headers);

            // set recipient to the skill
            var recipientId = activity.Recipient.Id;

            activity.Recipient.Id = skillManifest.MSAappId;

            // Serialize the activity and POST to the Skill endpoint
            var body    = new StringContent(JsonConvert.SerializeObject(activity, SerializationSettings.BotSchemaSerializationSettings), Encoding.UTF8, SerializationSettings.ApplicationJson);
            var request = StreamingRequest.CreatePost(string.Empty, body);

            // set back recipient id to make things consistent
            activity.Recipient.Id = recipientId;

            var stopWatch = new System.Diagnostics.Stopwatch();

            stopWatch.Start();
            await _streamingTransportClient.SendAsync(request);

            stopWatch.Stop();

            _botTelemetryClient.TrackEvent(
                "SkillWebSocketTurnLatency",
                new Dictionary <string, string>
            {
                { "SkillName", skillManifest.Name },
                { "SkillEndpoint", skillManifest.Endpoint.ToString() },
            },
                new Dictionary <string, double>
            {
                { "Latency", stopWatch.ElapsedMilliseconds },
            });

            return(_handoffActivity);
        }
 public SkillWebSocketTransport(
     SkillManifest skillManifest,
     IServiceClientCredentials serviceCLientCredentials,
     IStreamingTransportClient streamingTransportClient = null)
 {
     _skillManifest            = skillManifest ?? throw new ArgumentNullException(nameof(skillManifest));
     _serviceClientCredentials = serviceCLientCredentials ?? throw new ArgumentNullException(nameof(serviceCLientCredentials));
     _streamingTransportClient = streamingTransportClient;
 }
Esempio n. 5
0
        public async Task CancelRemoteDialogsAsync(SkillManifest skillManifest, IServiceClientCredentials appCredentials, ITurnContext turnContext)
        {
            var cancelRemoteDialogEvent = turnContext.Activity.CreateReply();

            cancelRemoteDialogEvent.Type = ActivityTypes.Event;
            cancelRemoteDialogEvent.Name = SkillEvents.CancelAllSkillDialogsEventName;

            await ForwardToSkillAsync(skillManifest, appCredentials, turnContext, cancelRemoteDialogEvent).ConfigureAwait(false);
        }
Esempio n. 6
0
        // This method creates a MultiProviderAuthDialog based on a skill manifest.
        private MultiProviderAuthDialog BuildAuthDialog(SkillManifest skill, BotSettings settings, MicrosoftAppCredentials appCredentials)
        {
            if (skill.AuthenticationConnections?.Count() > 0)
            {
                if (settings.OAuthConnections.Any() && settings.OAuthConnections.Any(o => skill.AuthenticationConnections.Any(s => s.ServiceProviderId == o.Provider)))
                {
                    var oauthConnections = settings.OAuthConnections.Where(o => skill.AuthenticationConnections.Any(s => s.ServiceProviderId == o.Provider)).ToList();
                    return(new MultiProviderAuthDialog(oauthConnections, appCredentials));
                }
                else
                {
                    throw new Exception($"You must configure at least one supported OAuth connection to use this skill: {skill.Name}.");
                }
            }

            return(null);
        }
        public void AddSkillManifest()
        {
            // Simple skill, no slots
            _skillManifest = ManifestUtilities.CreateSkill(
                "testSkill",
                "testSkill",
                "https://testskill.tempuri.org/api/skill",
                "testSkill/testAction");

            // Add the SkillDialog to the available dialogs passing the initialized FakeSkill
            Dialogs.Add(new SkillDialogTest(
                            _skillManifest,
                            _mockServiceClientCredentials,
                            _mockTelemetryClient,
                            UserState,
                            _mockSkillTransport));
        }
Esempio n. 8
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SkillDialog"/> class.
        /// SkillDialog constructor that accepts the manifest description of a Skill along with TelemetryClient for end to end telemetry.
        /// </summary>
        /// <param name="skillManifest">Skill manifest.</param>
        /// <param name="serviceClientCredentials">Service client credentials.</param>
        /// <param name="telemetryClient">Telemetry Client.</param>
        /// <param name="userState">User State.</param>
        /// <param name="authDialog">Auth Dialog.</param>
        /// <param name="skillTransport">Transport used for skill invocation.</param>
        public SkillDialog(
            SkillManifest skillManifest,
            IServiceClientCredentials serviceClientCredentials,
            IBotTelemetryClient telemetryClient,
            UserState userState,
            MultiProviderAuthDialog authDialog = null,
            ISkillTransport skillTransport     = null)
            : base(skillManifest.Id)
        {
            _skillManifest            = skillManifest ?? throw new ArgumentNullException(nameof(SkillManifest));
            _serviceClientCredentials = serviceClientCredentials ?? throw new ArgumentNullException(nameof(serviceClientCredentials));
            _telemetryClient          = telemetryClient ?? throw new ArgumentNullException(nameof(telemetryClient));
            _userState      = userState;
            _skillTransport = skillTransport ?? new SkillWebSocketTransport(_skillManifest, _serviceClientCredentials);

            if (authDialog != null)
            {
                _authDialog = authDialog;
                AddDialog(authDialog);
            }
        }
Esempio n. 9
0
        public TestFlow GetTestFlow(SkillManifest skillManifest, string actionId, Dictionary <string, JObject> slots)
        {
            var sp      = Services.BuildServiceProvider();
            var adapter = sp.GetService <TestAdapter>();

            var testFlow = new TestFlow(adapter, async(context, token) =>
            {
                var dc        = await Dialogs.CreateContextAsync(context);
                var userState = await SkillContextAccessor.GetAsync(dc.Context, () => new SkillContext());

                // If we have SkillContext data to populate
                if (slots != null)
                {
                    // Add state to the SKillContext
                    foreach (var slot in slots)
                    {
                        userState[slot.Key] = slot.Value;
                    }
                }

                if (dc.ActiveDialog != null)
                {
                    var result = await dc.ContinueDialogAsync();
                }
                else
                {
                    // ActionID lets the SkillDialog know which action to call
                    await dc.BeginDialogAsync(skillManifest.Id, actionId);

                    // We don't continue as we don't care about the message being sent
                    // just the initial instantiation, we need to send a message within tests
                    // to invoke the flow. If continue is called then HttpMocks need be updated
                    // to handle the subsequent activity "ack"
                    // var result = await dc.ContinueDialogAsync();
                }
            });

            return(testFlow);
        }
        public Task <Activity> ForwardToSkillAsync(SkillManifest skillManifest, IServiceClientCredentials serviceClientCredentials, ITurnContext dialogContext, Activity activity, Action <Activity> tokenRequestHandler = null)
        {
            _activityForwarded = activity;

            return(Task.FromResult <Activity>(null));
        }
 public Task CancelRemoteDialogsAsync(SkillManifest skillManifest, IServiceClientCredentials serviceClientCredentials, ITurnContext turnContext)
 {
     return(Task.CompletedTask);
 }
Esempio n. 12
0
 public SkillDialogTest(SkillManifest skillManifest, IServiceClientCredentials serviceClientCredentials, IBotTelemetryClient telemetryClient, UserState userState, ISkillTransport skillTransport = null)
     : base(skillManifest, serviceClientCredentials, telemetryClient, userState, null, skillTransport)
 {
 }
Esempio n. 13
0
        public async Task <SkillManifest> GenerateManifestAsync(string manifestFile, string appId, Dictionary <string, BotSettingsBase.CognitiveModelConfiguration> cognitiveModels, string uriBase, bool inlineTriggerUtterances = false)
        {
            SkillManifest skillManifest = null;

            if (string.IsNullOrEmpty(manifestFile))
            {
                throw new ArgumentNullException(nameof(manifestFile));
            }

            if (string.IsNullOrEmpty(appId))
            {
                throw new ArgumentNullException(nameof(appId));
            }

            if (cognitiveModels == null)
            {
                throw new ArgumentNullException(nameof(cognitiveModels));
            }

            if (string.IsNullOrEmpty(uriBase))
            {
                throw new ArgumentNullException(nameof(uriBase));
            }

            // Each skill has a manifest template in the root directory and is used as foundation for the generated manifest
            using (StreamReader reader = new StreamReader(manifestFile))
            {
                skillManifest = JsonConvert.DeserializeObject <SkillManifest>(reader.ReadToEnd());

                if (string.IsNullOrEmpty(skillManifest.Id))
                {
                    throw new Exception("Skill manifest ID property was not present in the template manifest file.");
                }

                if (string.IsNullOrEmpty(skillManifest.Name))
                {
                    throw new Exception("Skill manifest Name property was not present in the template manifest file.");
                }

                skillManifest.MSAappId = appId;
                skillManifest.Endpoint = new Uri($"{uriBase}{_skillRoute}");

                if (skillManifest.IconUrl != null)
                {
                    skillManifest.IconUrl = new Uri($"{uriBase}/{skillManifest.IconUrl.ToString()}");
                }

                // The manifest can either return a pointer to the triggering utterances or include them inline in the manifest
                // If the developer has requested inline, we need to go through all utteranceSource references and retrieve the utterances and insert inline
                if (inlineTriggerUtterances)
                {
                    Dictionary <string, dynamic> localeLuisModels = new Dictionary <string, dynamic>();

                    // Retrieve all of the LUIS model definitions deployed and configured for the skill which could have multiple locales
                    // These are used to match the model name and intent so we can retrieve the utterances
                    foreach (var localeSet in cognitiveModels)
                    {
                        // Download/cache all the LUIS models configured for this locale (key is the locale name)
                        await PreFetchLuisModelContentsAsync(localeLuisModels, localeSet.Key, localeSet.Value.LanguageModels).ConfigureAwait(false);
                    }

                    foreach (var action in skillManifest.Actions)
                    {
                        // Is this Action triggerd by LUIS utterances rather than events?
                        if (action.Definition.Triggers.UtteranceSources != null)
                        {
                            // We will retrieve all utterances from the referenced source and aggregate into one new aggregated list of utterances per action
                            action.Definition.Triggers.Utterances = new List <Utterance>();
                            var utterancesToAdd = new List <string>();

                            // Iterate through each utterance source, one per locale.
                            foreach (UtteranceSource utteranceSource in action.Definition.Triggers.UtteranceSources)
                            {
                                // There may be multiple intents linked to this
                                foreach (var source in utteranceSource.Source)
                                {
                                    // Retrieve the intent mapped to this action trigger
                                    var intentIndex = source.IndexOf('#');
                                    if (intentIndex == -1)
                                    {
                                        throw new Exception($"Utterance source for action: {action.Id} didn't include an intent reference: {source}");
                                    }

                                    // We now have the name of the LUIS model and the Intent
                                    var    modelName     = source.Substring(0, intentIndex);
                                    string intentToMatch = source.Substring(intentIndex + 1);

                                    // Find the LUIS model from our cache by matching on the locale/modelname
                                    var model = localeLuisModels.SingleOrDefault(m => string.Equals(m.Key, $"{utteranceSource.Locale}_{modelName}", StringComparison.CurrentCultureIgnoreCase)).Value;
                                    if (model == null)
                                    {
                                        throw new Exception($"Utterance source (locale: {utteranceSource.Locale}) for action: '{action.Id}' references the '{modelName}' model which cannot be found in the currently deployed configuration.");
                                    }

                                    // Validate that the intent in the manifest exists in this LUIS model
                                    IEnumerable <JToken> intents = model.intents;

                                    if (!intents.Any(i => string.Equals(i["name"].ToString(), intentToMatch, StringComparison.CurrentCultureIgnoreCase)))
                                    {
                                        throw new Exception($"Utterance source for action: '{action.Id}' references the '{modelName}' model and '{intentToMatch}' intent which does not exist.");
                                    }

                                    // Retrieve the utterances that match this intent
                                    IEnumerable <JToken> utterancesList = model.utterances;
                                    var utterances = utterancesList.Where(s => string.Equals(s["intent"].ToString(), intentToMatch, StringComparison.CurrentCultureIgnoreCase));

                                    if (!utterances.Any())
                                    {
                                        throw new Exception($"Utterance source for action: '{action.Id}' references the '{modelName}' model and '{intentToMatch}' intent which has no utterances.");
                                    }

                                    foreach (JObject utterance in utterances)
                                    {
                                        utterancesToAdd.Add(utterance["text"].Value <string>());
                                    }
                                }

                                action.Definition.Triggers.Utterances.Add(new Utterance(utteranceSource.Locale, utterancesToAdd.ToArray()));
                            }
                        }
                    }
                }
            }

            return(skillManifest);
        }
Esempio n. 14
0
 public SkillHttpTransport(SkillManifest skillManifest, IServiceClientCredentials serviceClientCredentials)
 {
     _skillManifest            = skillManifest ?? throw new ArgumentNullException(nameof(skillManifest));
     _serviceClientCredentials = serviceClientCredentials ?? throw new ArgumentNullException(nameof(serviceClientCredentials));
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="SwitchSkillDialogOptions"/> class.
 /// </summary>
 /// <param name="prompt">The <see cref="Activity"/> to display when prompting to switch skills.</param>
 /// <param name="manifest">The <see cref="SkillManifest"/> for the new skill.</param>
 public SwitchSkillDialogOptions(Activity prompt, SkillManifest manifest)
 {
     Prompt = prompt;
     Skill  = manifest;
 }