From bf14fd9cb16a9fe9575eb857e427c94e23eb125a Mon Sep 17 00:00:00 2001 From: Hung-chih Yang Date: Tue, 29 Jun 2021 11:15:38 -0700 Subject: [PATCH 1/7] Fix bug that whether an Orchestrator object can entract entities or not should not depend on a member variable ScoreEntities. --- .../OrchestratorRecognizer.cs | 65 ++++++++++++++----- .../OrchestratorAdaptiveRecognizerTests.cs | 1 - 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs b/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs index 561b87ee1f..46b3f8b826 100644 --- a/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs +++ b/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs @@ -42,9 +42,10 @@ public class OrchestratorRecognizer : AdaptiveRecognizer /// public const string EntitiesProperty = "entityResult"; private const float UnknownIntentFilterScore = 0.4F; - private static ConcurrentDictionary orchestratorMap = new ConcurrentDictionary(); + private static ConcurrentDictionary orchestratorMap = new ConcurrentDictionary(); private string _modelFolder; private string _snapshotFile; + private OrchestratorDictionaryEntry _orchestratorDictionaryEntry = null; private ILabelResolver _resolver = null; /// @@ -82,11 +83,6 @@ public OrchestratorRecognizer(string modelFolder, string snapshotFile, ILabelRes InitializeModel(); } - [JsonIgnore] -#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member - public bool ScoreEntities { get; set; } = false; -#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member - /// /// Gets or sets the folder path to Orchestrator base model to use. /// @@ -230,7 +226,7 @@ public override async Task RecognizeAsync(DialogContext dc, Sc // Return 'None' if no intent matched. recognizerResult.Intents.Add(NoneIntent, new IntentScore() { Score = 1.0 }); } - + if (ExternalEntityRecognizer != null) { // Run external recognition @@ -239,7 +235,7 @@ public override async Task RecognizeAsync(DialogContext dc, Sc } TryScoreEntities(text, recognizerResult); - + // Add full recognition result as a 'result' property await dc.Context.TraceActivityAsync($"{nameof(OrchestratorRecognizer)}Result", JObject.FromObject(recognizerResult), nameof(OrchestratorRecognizer), "Orchestrator Recognition", cancellationToken).ConfigureAwait(false); TrackRecognizerResult(dc, $"{nameof(OrchestratorRecognizer)}Result", FillRecognizerResultTelemetryProperties(recognizerResult, telemetryProperties, dc), telemetryMetrics); @@ -317,7 +313,7 @@ private static JToken EntityResultToInstanceJObject(string text, Result result) private void TryScoreEntities(string text, RecognizerResult recognizerResult) { - if (!this.ScoreEntities) + if (!this._orchestratorDictionaryEntry.IsEntityReady) { return; } @@ -389,23 +385,28 @@ private void InitializeModel() var fullModelFolder = Path.GetFullPath(PathUtils.NormalizePath(_modelFolder)); - var orchestrator = orchestratorMap.GetOrAdd(fullModelFolder, path => + _orchestratorDictionaryEntry = orchestratorMap.GetOrAdd(fullModelFolder, path => { // Create Orchestrator - string entityModelFolder = null; + string entityModelFolder = null; + bool isEntityReady = false; try { entityModelFolder = Path.Combine(path, "entity"); - ScoreEntities = Directory.Exists(entityModelFolder); + isEntityReady = Directory.Exists(entityModelFolder); - return ScoreEntities ? - new BotFramework.Orchestrator.Orchestrator(path, entityModelFolder) : - new BotFramework.Orchestrator.Orchestrator(path); + return new OrchestratorDictionaryEntry() + { + Orchestrator = isEntityReady ? + new BotFramework.Orchestrator.Orchestrator(path, entityModelFolder) : + new BotFramework.Orchestrator.Orchestrator(path), + IsEntityReady = isEntityReady + }; } catch (Exception ex) { throw new InvalidOperationException( - ScoreEntities ? $"Failed to find or load Model with path {path}, entity model path {entityModelFolder}" : $"Failed to find or load Model with path {path}", + isEntityReady ? $"Failed to find or load Model with path {path}, entity model path {entityModelFolder}" : $"Failed to find or load Model with path {path}", ex); } }); @@ -417,7 +418,37 @@ private void InitializeModel() byte[] snapShotByteArray = Encoding.UTF8.GetBytes(content); // Create label resolver - _resolver = orchestrator.CreateLabelResolver(snapShotByteArray); + _resolver = _orchestratorDictionaryEntry.Orchestrator.CreateLabelResolver(snapShotByteArray); + } + + /// + /// OrchestratorDictionaryEntry is used in a static OrchestratorMap object. + /// + protected class OrchestratorDictionaryEntry + { + /// + /// Gets or sets the Orchestrator object. + /// + /// + /// The Orchestrator object. + /// + public BotFramework.Orchestrator.Orchestrator Orchestrator + { + get; + protected internal set; + } + + /// + /// Gets or sets a value indicating whether the Orchestrator object ready for entity extraction. + /// + /// + /// The IsEntityReady flag. + /// + public bool IsEntityReady + { + get; + protected internal set; + } } } } diff --git a/tests/Microsoft.Bot.Builder.AI.Orchestrator.Tests/OrchestratorAdaptiveRecognizerTests.cs b/tests/Microsoft.Bot.Builder.AI.Orchestrator.Tests/OrchestratorAdaptiveRecognizerTests.cs index 75724b33d1..9192d8e5ca 100644 --- a/tests/Microsoft.Bot.Builder.AI.Orchestrator.Tests/OrchestratorAdaptiveRecognizerTests.cs +++ b/tests/Microsoft.Bot.Builder.AI.Orchestrator.Tests/OrchestratorAdaptiveRecognizerTests.cs @@ -166,7 +166,6 @@ public async Task TestEntityRecognize() { ModelFolder = new StringExpression("fakePath"), SnapshotFile = new StringExpression("fakePath"), - ScoreEntities = true, ExternalEntityRecognizer = new NumberEntityRecognizer() }; From 66382e3e3f07408ed9f35fcdf8967a6a95e05ccc Mon Sep 17 00:00:00 2001 From: Hung-chih Yang Date: Tue, 29 Jun 2021 11:53:49 -0700 Subject: [PATCH 2/7] logic for fixing unit tests --- .../OrchestratorRecognizer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs b/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs index 46b3f8b826..e708c8c64e 100644 --- a/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs +++ b/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs @@ -313,7 +313,7 @@ private static JToken EntityResultToInstanceJObject(string text, Result result) private void TryScoreEntities(string text, RecognizerResult recognizerResult) { - if (!this._orchestratorDictionaryEntry.IsEntityReady) + if ((this._orchestratorDictionaryEntry == null) || (!this._orchestratorDictionaryEntry.IsEntityReady)) { return; } From 452c0ad9229eb124cc794f00faf9e6f825ddb281 Mon Sep 17 00:00:00 2001 From: Hung-chih Yang Date: Tue, 29 Jun 2021 12:57:30 -0700 Subject: [PATCH 3/7] Fix TryScoreEntities() for unit tests. --- .../OrchestratorRecognizer.cs | 71 ++++++++++--------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs b/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs index e708c8c64e..19ec7f1192 100644 --- a/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs +++ b/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs @@ -313,53 +313,60 @@ private static JToken EntityResultToInstanceJObject(string text, Result result) private void TryScoreEntities(string text, RecognizerResult recognizerResult) { - if ((this._orchestratorDictionaryEntry == null) || (!this._orchestratorDictionaryEntry.IsEntityReady)) + if (this._resolver == null) { return; } - var results = _resolver.Score(text, LabelType.Entity); - recognizerResult.Properties.Add(EntitiesProperty, results); - - if (results.Any()) + try { - if (recognizerResult.Entities == null) - { - recognizerResult.Entities = new JObject(); - } + var results = _resolver.Score(text, LabelType.Entity); + recognizerResult.Properties.Add(EntitiesProperty, results); - var entitiesResult = recognizerResult.Entities; - foreach (var result in results) + if (results.Any()) { - // add value - JToken values; - if (!entitiesResult.TryGetValue(result.Label.Name, StringComparison.OrdinalIgnoreCase, out values)) + if (recognizerResult.Entities == null) { - values = new JArray(); - entitiesResult[result.Label.Name] = values; + recognizerResult.Entities = new JObject(); } - ((JArray)values).Add(EntityResultToJObject(text, result)); - - // get/create $instance - JToken instanceRoot; - if (!recognizerResult.Entities.TryGetValue("$instance", StringComparison.OrdinalIgnoreCase, out instanceRoot)) + var entitiesResult = recognizerResult.Entities; + foreach (var result in results) { - instanceRoot = new JObject(); - recognizerResult.Entities["$instance"] = instanceRoot; - } + // add value + JToken values; + if (!entitiesResult.TryGetValue(result.Label.Name, StringComparison.OrdinalIgnoreCase, out values)) + { + values = new JArray(); + entitiesResult[result.Label.Name] = values; + } - // add instanceData - JToken instanceData; - if (!((JObject)instanceRoot).TryGetValue(result.Label.Name, StringComparison.OrdinalIgnoreCase, out instanceData)) - { - instanceData = new JArray(); - instanceRoot[result.Label.Name] = instanceData; - } + ((JArray)values).Add(EntityResultToJObject(text, result)); + + // get/create $instance + JToken instanceRoot; + if (!recognizerResult.Entities.TryGetValue("$instance", StringComparison.OrdinalIgnoreCase, out instanceRoot)) + { + instanceRoot = new JObject(); + recognizerResult.Entities["$instance"] = instanceRoot; + } + + // add instanceData + JToken instanceData; + if (!((JObject)instanceRoot).TryGetValue(result.Label.Name, StringComparison.OrdinalIgnoreCase, out instanceData)) + { + instanceData = new JArray(); + instanceRoot[result.Label.Name] = instanceData; + } - ((JArray)instanceData).Add(EntityResultToInstanceJObject(text, result)); + ((JArray)instanceData).Add(EntityResultToInstanceJObject(text, result)); + } } } + catch (ApplicationException) + { + return; // ---- This is a "Try" function, i.e., best effort only, no exception. + } } private void InitializeModel() From 354db18c37b7264784ce6ad3e60abe4ea1175f4d Mon Sep 17 00:00:00 2001 From: Hung-chih Yang Date: Tue, 29 Jun 2021 13:44:04 -0700 Subject: [PATCH 4/7] Remove logic for checking entity capability --- .../OrchestratorRecognizer.cs | 47 +++---------------- 1 file changed, 6 insertions(+), 41 deletions(-) diff --git a/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs b/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs index 19ec7f1192..92bbdce99c 100644 --- a/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs +++ b/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs @@ -42,10 +42,9 @@ public class OrchestratorRecognizer : AdaptiveRecognizer /// public const string EntitiesProperty = "entityResult"; private const float UnknownIntentFilterScore = 0.4F; - private static ConcurrentDictionary orchestratorMap = new ConcurrentDictionary(); + private static ConcurrentDictionary orchestratorMap = new ConcurrentDictionary(); private string _modelFolder; private string _snapshotFile; - private OrchestratorDictionaryEntry _orchestratorDictionaryEntry = null; private ILabelResolver _resolver = null; /// @@ -392,7 +391,7 @@ private void InitializeModel() var fullModelFolder = Path.GetFullPath(PathUtils.NormalizePath(_modelFolder)); - _orchestratorDictionaryEntry = orchestratorMap.GetOrAdd(fullModelFolder, path => + BotFramework.Orchestrator.Orchestrator orchestrator = orchestratorMap.GetOrAdd(fullModelFolder, path => { // Create Orchestrator string entityModelFolder = null; @@ -402,13 +401,9 @@ private void InitializeModel() entityModelFolder = Path.Combine(path, "entity"); isEntityReady = Directory.Exists(entityModelFolder); - return new OrchestratorDictionaryEntry() - { - Orchestrator = isEntityReady ? - new BotFramework.Orchestrator.Orchestrator(path, entityModelFolder) : - new BotFramework.Orchestrator.Orchestrator(path), - IsEntityReady = isEntityReady - }; + return isEntityReady ? + new BotFramework.Orchestrator.Orchestrator(path, entityModelFolder) : + new BotFramework.Orchestrator.Orchestrator(path); } catch (Exception ex) { @@ -425,37 +420,7 @@ private void InitializeModel() byte[] snapShotByteArray = Encoding.UTF8.GetBytes(content); // Create label resolver - _resolver = _orchestratorDictionaryEntry.Orchestrator.CreateLabelResolver(snapShotByteArray); - } - - /// - /// OrchestratorDictionaryEntry is used in a static OrchestratorMap object. - /// - protected class OrchestratorDictionaryEntry - { - /// - /// Gets or sets the Orchestrator object. - /// - /// - /// The Orchestrator object. - /// - public BotFramework.Orchestrator.Orchestrator Orchestrator - { - get; - protected internal set; - } - - /// - /// Gets or sets a value indicating whether the Orchestrator object ready for entity extraction. - /// - /// - /// The IsEntityReady flag. - /// - public bool IsEntityReady - { - get; - protected internal set; - } + _resolver = orchestrator.CreateLabelResolver(snapShotByteArray); } } } From 340ce7cd4c71ce01e66ba1c30bd73da713546d1b Mon Sep 17 00:00:00 2001 From: Hung-chih Yang Date: Tue, 29 Jun 2021 23:40:43 -0700 Subject: [PATCH 5/7] refactor code for thread safety --- .../OrchestratorRecognizer.cs | 153 ++++++++++++------ 1 file changed, 103 insertions(+), 50 deletions(-) diff --git a/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs b/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs index 92bbdce99c..7deb7ca067 100644 --- a/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs +++ b/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs @@ -42,10 +42,10 @@ public class OrchestratorRecognizer : AdaptiveRecognizer /// public const string EntitiesProperty = "entityResult"; private const float UnknownIntentFilterScore = 0.4F; - private static ConcurrentDictionary orchestratorMap = new ConcurrentDictionary(); - private string _modelFolder; - private string _snapshotFile; + private static ConcurrentDictionary orchestratorMap = new ConcurrentDictionary(); + private OrchestratorDictionaryEntry _orchestrator = null; private ILabelResolver _resolver = null; + private bool _isResolverMockup = false; /// /// Initializes a new instance of the class. @@ -63,23 +63,10 @@ public OrchestratorRecognizer([CallerFilePath] string callerPath = "", [CallerLi /// /// Specifies the base model folder. /// Specifies full path to the snapshot file. - /// Label resolver. - public OrchestratorRecognizer(string modelFolder, string snapshotFile, ILabelResolver resolver = null) + /// Label resolver mockup. + public OrchestratorRecognizer(string modelFolder, string snapshotFile, ILabelResolver resolverMockup = null) { - _resolver = resolver; - if (modelFolder == null) - { - throw new ArgumentNullException(nameof(modelFolder)); - } - - if (snapshotFile == null) - { - throw new ArgumentNullException(nameof(snapshotFile)); - } - - _modelFolder = modelFolder; - _snapshotFile = snapshotFile; - InitializeModel(); + InitializeModel(modelFolder, snapshotFile, resolverMockup); } /// @@ -126,6 +113,15 @@ public OrchestratorRecognizer(string modelFolder, string snapshotFile, ILabelRes [JsonProperty("detectAmbiguousIntents")] public BoolExpression DetectAmbiguousIntents { get; set; } = false; + /// + /// Gets or sets a value indicating whether to enable or disable entity-extraction logic. + /// NOTE: SHOULD consider removing this flag in the next major SDK release (V5). + /// + /// + /// The flag for enabling or disabling entity-extraction function. + /// + public bool ScoreEntities { get; set; } = true; + /// /// Return recognition results. /// @@ -137,14 +133,16 @@ public OrchestratorRecognizer(string modelFolder, string snapshotFile, ILabelRes /// A containing the QnA Maker result. public override async Task RecognizeAsync(DialogContext dc, Schema.Activity activity, CancellationToken cancellationToken, Dictionary telemetryProperties = null, Dictionary telemetryMetrics = null) { + if (_resolver == null) + { + string modelFolder = ModelFolder.GetValue(dc.State); + string snapshotFile = SnapshotFile.GetValue(dc.State); + InitializeModel(modelFolder, snapshotFile, null); + } + var text = activity.Text ?? string.Empty; var detectAmbiguity = DetectAmbiguousIntents.GetValue(dc.State); - _modelFolder = ModelFolder.GetValue(dc.State); - _snapshotFile = SnapshotFile.GetValue(dc.State); - - InitializeModel(); - var recognizerResult = new RecognizerResult() { Text = text, @@ -312,11 +310,32 @@ private static JToken EntityResultToInstanceJObject(string text, Result result) private void TryScoreEntities(string text, RecognizerResult recognizerResult) { - if (this._resolver == null) + // It's impossible to extract entities without a _resolver object. + if (_resolver == null) { return; } + // Entity extraction can be controlled by the ScoreEntities flag. + // NOTE: SHOULD consider removing this flag in the next major SDK release (V5). + if (!this.ScoreEntities) + { + return; + } + + // The following check is necessary to ensure that the _resolver object + // is capable of entity exttraction. However, this check can also block + // a mock-up _resolver. + if (!_isResolverMockup) + { + if ((_orchestrator == null) || (!_orchestrator.IsEntityExtractionCapable)) + { + return; + } + } + + // As this method is TryScoreEntities, so it's best effort only, there should + // not be any exception thrown out of this method. try { var results = _resolver.Score(text, LabelType.Entity); @@ -368,59 +387,93 @@ private void TryScoreEntities(string text, RecognizerResult recognizerResult) } } - private void InitializeModel() + [MethodImpl(MethodImplOptions.Synchronized)] + private void InitializeModel(string modelFolder, string snapshotFile, ILabelResolver resolverMockup = null) { - if (_modelFolder == null) + if (resolverMockup != null) { -#pragma warning disable CA2208 // Instantiate argument exceptions correctly - throw new ArgumentNullException("ModelFolder"); -#pragma warning restore CA2208 // Instantiate argument exceptions correctly + _resolver = resolverMockup; + _isResolverMockup = true; + return; } - if (_snapshotFile == null) { -#pragma warning disable CA2208 // Instantiate argument exceptions correctly - throw new ArgumentNullException("SnapshotFile"); -#pragma warning restore CA2208 // Instantiate argument exceptions correctly - } + if (modelFolder == null) + { + throw new ArgumentNullException(nameof(modelFolder)); + } - if (_resolver != null) - { - return; + if (snapshotFile == null) + { + throw new ArgumentNullException(nameof(snapshotFile)); + } } - var fullModelFolder = Path.GetFullPath(PathUtils.NormalizePath(_modelFolder)); + var fullModelFolder = Path.GetFullPath(PathUtils.NormalizePath(modelFolder)); - BotFramework.Orchestrator.Orchestrator orchestrator = orchestratorMap.GetOrAdd(fullModelFolder, path => + _orchestrator = orchestratorMap.GetOrAdd(fullModelFolder, path => { // Create Orchestrator string entityModelFolder = null; - bool isEntityReady = false; + bool isEntityExtractionCapable = false; try { entityModelFolder = Path.Combine(path, "entity"); - isEntityReady = Directory.Exists(entityModelFolder); + isEntityExtractionCapable = Directory.Exists(entityModelFolder); - return isEntityReady ? - new BotFramework.Orchestrator.Orchestrator(path, entityModelFolder) : - new BotFramework.Orchestrator.Orchestrator(path); + return new OrchestratorDictionaryEntry() + { + Orchestrator = isEntityExtractionCapable ? + new BotFramework.Orchestrator.Orchestrator(path, entityModelFolder) : + new BotFramework.Orchestrator.Orchestrator(path), + IsEntityExtractionCapable = isEntityExtractionCapable + }; } catch (Exception ex) { throw new InvalidOperationException( - isEntityReady ? $"Failed to find or load Model with path {path}, entity model path {entityModelFolder}" : $"Failed to find or load Model with path {path}", + isEntityExtractionCapable ? $"Failed to find or load Model with path {path}, entity model path {entityModelFolder}" : $"Failed to find or load Model with path {path}", ex); } }); - var fullSnapShotFile = Path.GetFullPath(PathUtils.NormalizePath(_snapshotFile)); + var fullSnapShotFile = Path.GetFullPath(PathUtils.NormalizePath(snapshotFile)); // Load the snapshot - string content = File.ReadAllText(fullSnapShotFile); - byte[] snapShotByteArray = Encoding.UTF8.GetBytes(content); + byte[] snapShotByteArray = File.ReadAllBytes(fullSnapShotFile); // Create label resolver - _resolver = orchestrator.CreateLabelResolver(snapShotByteArray); + _resolver = this._orchestrator.Orchestrator.CreateLabelResolver(snapShotByteArray); + } + + /// + /// OrchestratorDictionaryEntry is used for the static orchestratorMap object. + /// + private class OrchestratorDictionaryEntry + { + /// + /// Gets or sets the Orchestrator object. + /// + /// + /// The Orchestrator object. + /// + public BotFramework.Orchestrator.Orchestrator Orchestrator + { + get; + set; + } + + /// + /// Gets or sets a value indicating whether the Orchestrator object is capable of entity extraction. + /// + /// + /// The IsEntityExtractionCapable flag. + /// + public bool IsEntityExtractionCapable + { + get; + set; + } } } } From 0a11fbd287cb4755f4791afda35bcb7cb97ebfcc Mon Sep 17 00:00:00 2001 From: Hung-chih Yang Date: Wed, 30 Jun 2021 00:29:31 -0700 Subject: [PATCH 6/7] refined error checking --- .../OrchestratorRecognizer.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs b/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs index 7deb7ca067..c7296ccdf2 100644 --- a/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs +++ b/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs @@ -158,11 +158,11 @@ public override async Task RecognizeAsync(DialogContext dc, Sc // Score with orchestrator var results = _resolver.Score(text); - // Add full recognition result as a 'result' property - recognizerResult.Properties.Add(ResultProperty, results); - - if (results.Any()) + if ((results != null) && results.Any()) { + // Add full recognition result as a 'result' property + recognizerResult.Properties.Add(ResultProperty, results); + var topScore = results[0].Score; // if top scoring intent is less than threshold, return None @@ -339,10 +339,11 @@ private void TryScoreEntities(string text, RecognizerResult recognizerResult) try { var results = _resolver.Score(text, LabelType.Entity); - recognizerResult.Properties.Add(EntitiesProperty, results); - if (results.Any()) + if ((results != null) && results.Any()) { + recognizerResult.Properties.Add(EntitiesProperty, results); + if (recognizerResult.Entities == null) { recognizerResult.Entities = new JObject(); @@ -398,12 +399,12 @@ private void InitializeModel(string modelFolder, string snapshotFile, ILabelReso } { - if (modelFolder == null) + if (string.IsNullOrWhiteSpace(modelFolder)) { throw new ArgumentNullException(nameof(modelFolder)); } - if (snapshotFile == null) + if (string.IsNullOrWhiteSpace(snapshotFile)) { throw new ArgumentNullException(nameof(snapshotFile)); } From ca180a83a478458e0e98e195e4ad158910d47e74 Mon Sep 17 00:00:00 2001 From: Hung-chih Yang Date: Wed, 30 Jun 2021 10:17:45 -0700 Subject: [PATCH 7/7] minor rename --- .../OrchestratorRecognizer.cs | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs b/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs index c7296ccdf2..364ddc8af4 100644 --- a/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs +++ b/libraries/Microsoft.Bot.Builder.AI.Orchestrator/OrchestratorRecognizer.cs @@ -63,10 +63,10 @@ public OrchestratorRecognizer([CallerFilePath] string callerPath = "", [CallerLi /// /// Specifies the base model folder. /// Specifies full path to the snapshot file. - /// Label resolver mockup. - public OrchestratorRecognizer(string modelFolder, string snapshotFile, ILabelResolver resolverMockup = null) + /// External label resolver object. + public OrchestratorRecognizer(string modelFolder, string snapshotFile, ILabelResolver resolverExternal = null) { - InitializeModel(modelFolder, snapshotFile, resolverMockup); + InitializeModel(modelFolder, snapshotFile, resolverExternal); } /// @@ -360,6 +360,12 @@ private void TryScoreEntities(string text, RecognizerResult recognizerResult) entitiesResult[result.Label.Name] = values; } + // values came from an external entity recognizer, which may not make it a JArray. + if (values.Type != JTokenType.Array) + { + values = new JArray(); + } + ((JArray)values).Add(EntityResultToJObject(text, result)); // get/create $instance @@ -370,6 +376,12 @@ private void TryScoreEntities(string text, RecognizerResult recognizerResult) recognizerResult.Entities["$instance"] = instanceRoot; } + // instanceRoot came from an external entity recognizer, which may not make it a JObject. + if (instanceRoot.Type != JTokenType.Object) + { + instanceRoot = new JObject(); + } + // add instanceData JToken instanceData; if (!((JObject)instanceRoot).TryGetValue(result.Label.Name, StringComparison.OrdinalIgnoreCase, out instanceData)) @@ -378,6 +390,12 @@ private void TryScoreEntities(string text, RecognizerResult recognizerResult) instanceRoot[result.Label.Name] = instanceData; } + // instanceData came from an external entity recognizer, which may not make it a JArray. + if (instanceData.Type != JTokenType.Array) + { + instanceData = new JArray(); + } + ((JArray)instanceData).Add(EntityResultToInstanceJObject(text, result)); } } @@ -389,11 +407,11 @@ private void TryScoreEntities(string text, RecognizerResult recognizerResult) } [MethodImpl(MethodImplOptions.Synchronized)] - private void InitializeModel(string modelFolder, string snapshotFile, ILabelResolver resolverMockup = null) + private void InitializeModel(string modelFolder, string snapshotFile, ILabelResolver resolverExternal = null) { - if (resolverMockup != null) + if (resolverExternal != null) { - _resolver = resolverMockup; + _resolver = resolverExternal; _isResolverMockup = true; return; }