diff --git a/CSharp/Blog-LUISActionBinding/LuisActions.Samples.Console/Program.cs b/CSharp/Blog-LUISActionBinding/LuisActions.Samples.Console/Program.cs index 8dae01fd1b..f8f70ad60a 100644 --- a/CSharp/Blog-LUISActionBinding/LuisActions.Samples.Console/Program.cs +++ b/CSharp/Blog-LUISActionBinding/LuisActions.Samples.Console/Program.cs @@ -94,7 +94,7 @@ private static async Task RunActions(ILuisService luisSe { Console.WriteLine($"Cannot execute action '{newActionDefinition.FriendlyName}' in the context of '{currentActionDefinition.FriendlyName}' - continuing with current action"); } - else + else if (!intentAction.GetType().Equals(queryResult.NewAction.GetType())) { var valid = LuisActionResolver.UpdateIfValidContextualAction(queryResult.NewAction, intentAction, out isContextual); if (!valid && isContextual) diff --git a/CSharp/Blog-LUISActionBinding/LuisActions.Samples.Shared/Advanced/AlternativesAction.cs b/CSharp/Blog-LUISActionBinding/LuisActions.Samples.Shared/Advanced/AlternativesAction.cs new file mode 100644 index 0000000000..15cb6e171d --- /dev/null +++ b/CSharp/Blog-LUISActionBinding/LuisActions.Samples.Shared/Advanced/AlternativesAction.cs @@ -0,0 +1,58 @@ +namespace LuisActions.Samples +{ + using System; + using System.ComponentModel.DataAnnotations; + using System.Threading.Tasks; + using Microsoft.Cognitive.LUIS.ActionBinding; + using Newtonsoft.Json; + + // This sample were added to showcase support to Custom List Entities + + // Refer to 'List Entities' @ https://docs.microsoft.com/en-us/azure/cognitive-services/luis/add-entities + + // You can create your own model with an 'AlternativeChoose' intent with utterances + // having a 'List Entity' called 'Alternatives' with the canonical forms defined by the + // 'Alternative' enum - or just can simply create your custom list entity, + // intent and update the intent binding here and custom name/type at fields + + // Note: The provided LUIS app @ LUIS_MODEL.json does not have an intent related to it + + public enum Alternative + { + // default (ie. empty choice) + None = 0, + + DomainOption1 = 11, + DomainOption2 = 12, + DomainOption3 = 13, + + ExternalOption1 = 101, + ExternalOption2 = 102 + } + + public class RequiredEnumAttribute : ValidationAttribute + { + public override bool IsValid(object value) + { + return (int)value > 0; + } + } + + [Serializable] + [LuisActionBinding("AlternativeChoose", FriendlyName = "Alternatives Choosing Model Sample")] + public class AlternativesAction : BaseLuisAction + { + [RequiredEnum(ErrorMessage = "Please provide an alternative")] + [LuisActionBindingParam(CustomType = "Alternatives", Order = 1)] + public Alternative FirstAlternative { get; set; } + + [Required(ErrorMessage = "Please provide one or more alternatives")] + [LuisActionBindingParam(CustomType = "Alternatives", Order = 2)] + public Alternative[] SecondaryAlternatives { get; set; } + + public override Task FulfillAsync() + { + return Task.FromResult((object)JsonConvert.SerializeObject(this)); + } + } +} diff --git a/CSharp/Blog-LUISActionBinding/LuisActions.Samples.Shared/LuisActions.Samples.Shared.csproj b/CSharp/Blog-LUISActionBinding/LuisActions.Samples.Shared/LuisActions.Samples.Shared.csproj index 0dbef98449..af8406667f 100644 --- a/CSharp/Blog-LUISActionBinding/LuisActions.Samples.Shared/LuisActions.Samples.Shared.csproj +++ b/CSharp/Blog-LUISActionBinding/LuisActions.Samples.Shared/LuisActions.Samples.Shared.csproj @@ -58,6 +58,7 @@ + diff --git a/CSharp/Blog-LUISActionBinding/Microsoft.Cognitive.LUIS.ActionBinding/LuisActionResolver.cs b/CSharp/Blog-LUISActionBinding/Microsoft.Cognitive.LUIS.ActionBinding/LuisActionResolver.cs index b998a5494e..c5a29b345f 100644 --- a/CSharp/Blog-LUISActionBinding/Microsoft.Cognitive.LUIS.ActionBinding/LuisActionResolver.cs +++ b/CSharp/Blog-LUISActionBinding/Microsoft.Cognitive.LUIS.ActionBinding/LuisActionResolver.cs @@ -9,6 +9,7 @@ using Microsoft.Bot.Builder.Dialogs.Internals; using Microsoft.Bot.Builder.Luis; using Microsoft.Bot.Builder.Luis.Models; + using Newtonsoft.Json.Linq; [Serializable] public class LuisActionResolver @@ -92,7 +93,7 @@ public static async Task QueryValueFromLuisAsync( { var newIntentName = default(string); var newAction = new LuisActionResolver(action.GetType().Assembly).ResolveActionFromLuisIntent(result, out newIntentName); - if (newAction != null) + if (newAction != null && !newAction.GetType().Equals(action.GetType())) { return new QueryValueResult(false) { @@ -348,20 +349,26 @@ private static bool AssignValue(ILuisAction action, PropertyInfo property, objec try { + object value; + + // handle LUIS JObjects + paramValue = SanitizeInputValue(type, paramValue); + if (type.IsArray) { - property.SetValue(action, BuildArrayOfValues(action, property, type.GetElementType(), paramValue)); + value = BuildArrayOfValues(action, property, type.GetElementType(), paramValue); } else if (type.IsEnum) { - property.SetValue(action, Enum.Parse(type, (string)paramValue)); + value = Enum.Parse(type, paramValue.ToString()); } else { - var value = Convert.ChangeType(paramValue, type); - property.SetValue(action, value); + value = Convert.ChangeType(paramValue, type); } + property.SetValue(action, value); + return true; } catch (FormatException) @@ -391,7 +398,7 @@ private static Array BuildArrayOfValues(ILuisAction action, PropertyInfo propert var result = Array.CreateInstance(elementType, values.Count()); foreach (var value in values) { - result.SetValue(elementType.IsEnum ? Enum.Parse(elementType, (string)value) : Convert.ChangeType(value, elementType), idx++); + result.SetValue(elementType.IsEnum ? Enum.Parse(elementType, value.ToString()) : Convert.ChangeType(value, elementType), idx++); } return result; @@ -402,6 +409,33 @@ private static Array BuildArrayOfValues(ILuisAction action, PropertyInfo propert } } + private static object SanitizeInputValue(Type targetType, object value) + { + object result = value; + + // handle case where input is JArray returned from LUIS + if (value is JArray) + { + var arrayOfValues = value as JArray; + + if (targetType.IsArray) + { + result = arrayOfValues.AsEnumerable(); + } + else + { + if (arrayOfValues.Count > 1) + { + throw new FormatException("Cannot assign multiple values to single field"); + } + + result = arrayOfValues[0]; + } + } + + return result; + } + private static bool AssignEntitiesToMembers( ILuisAction action, IEnumerable properties, @@ -490,6 +524,22 @@ private static bool AssignEntitiesToMembers( result &= AssignValue(action, property, paramValue); } + else if (matchingEntities.Count() > 0 + && matchingEntities.Count(e => e.Resolution != null && e.Resolution.First().Value is JArray) == matchingEntities.Count()) + { + var paramValues = new JArray(); + + foreach (var currentMatchingEntity in matchingEntities) + { + var values = currentMatchingEntity.Resolution.First().Value as JArray; + foreach (var value in values) + { + paramValues.Add(value); + } + } + + result &= AssignValue(action, property, paramValues); + } else { result = false;