/// <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; }
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); }
// 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)); }
/// <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); } }
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); }
public SkillDialogTest(SkillManifest skillManifest, IServiceClientCredentials serviceClientCredentials, IBotTelemetryClient telemetryClient, UserState userState, ISkillTransport skillTransport = null) : base(skillManifest, serviceClientCredentials, telemetryClient, userState, null, skillTransport) { }
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); }
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; }