diff --git a/Microsoft.Bot.Builder.sln b/Microsoft.Bot.Builder.sln
index e8760b4491..ebe7fffb5c 100644
--- a/Microsoft.Bot.Builder.sln
+++ b/Microsoft.Bot.Builder.sln
@@ -268,8 +268,8 @@ Global
{8D6F4046-0971-4E24-8E4D-F8175C6DFF2B}.Documentation|Any CPU.ActiveCfg = Documentation|Any CPU
{8D6F4046-0971-4E24-8E4D-F8175C6DFF2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8D6F4046-0971-4E24-8E4D-F8175C6DFF2B}.Release|Any CPU.Build.0 = Release|Any CPU
- {183B3324-4CFF-477A-8EAE-73953D3E383D}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug - NuGet Packages|Any CPU
- {183B3324-4CFF-477A-8EAE-73953D3E383D}.Debug - NuGet Packages|Any CPU.Build.0 = Debug - NuGet Packages|Any CPU
+ {183B3324-4CFF-477A-8EAE-73953D3E383D}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug|Any CPU
+ {183B3324-4CFF-477A-8EAE-73953D3E383D}.Debug - NuGet Packages|Any CPU.Build.0 = Debug|Any CPU
{183B3324-4CFF-477A-8EAE-73953D3E383D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{183B3324-4CFF-477A-8EAE-73953D3E383D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{183B3324-4CFF-477A-8EAE-73953D3E383D}.Documentation|Any CPU.ActiveCfg = Documentation|Any CPU
diff --git a/libraries/Microsoft.Bot.Builder.Ai.LUIS/Generator/DateTimeExpression.cs b/libraries/Microsoft.Bot.Builder.Ai.LUIS/Generator/DateTimeExpression.cs
new file mode 100644
index 0000000000..3d7dd25edb
--- /dev/null
+++ b/libraries/Microsoft.Bot.Builder.Ai.LUIS/Generator/DateTimeExpression.cs
@@ -0,0 +1,56 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.Bot.Builder.Ai.LUIS
+{
+ ///
+ /// Type for LUIS builtin_datetime.
+ ///
+ ///
+ /// LUIS recognizes time expressions like "next monday" and converts those to a type and set of timex expressions.
+ /// More information on timex can be found here: http://www.timeml.org/publications/timeMLdocs/timeml_1.2.1.html#timex3
+ /// More information on the library which does the recognition can be found here: https://github.com/Microsoft/Recognizers-Text
+ ///
+ public class DateTimeSpec
+ {
+ ///
+ /// Type of expression.
+ ///
+ /// Example types include:
+ ///
+ /// time -- simple time expression like "3pm".
+ /// date -- simple date like "july 3rd".
+ /// datetime -- combination of date and time like "march 23 2pm".
+ /// timerange -- a range of time like "2pm to 4pm".
+ /// daterange -- a range of dates like "march 23rd to 24th".
+ /// datetimerang -- a range of dates and times like "july 3rd 2pm to 5th 4pm".
+ /// set -- a recurrence like "every monday".
+ ///
+ ///
+ [JsonProperty("type")]
+ public readonly string Type;
+
+ ///
+ /// Timex expressions.
+ ///
+ [JsonProperty("timex")]
+ public readonly IList Expressions;
+
+ public DateTimeSpec(string type, IEnumerable expressions)
+ {
+ if (string.IsNullOrWhiteSpace(type)) throw new ArgumentNullException(nameof(type));
+ if (expressions == null) throw new ArgumentNullException(nameof(expressions));
+ Type = type;
+ Expressions = expressions.ToList();
+ }
+
+ public override string ToString()
+ {
+ return $"DateTimeSpec({Type}, [{String.Join(", ", Expressions)}]";
+ }
+ }
+}
diff --git a/libraries/Microsoft.Bot.Builder.Ai.LUIS/Generator/InstanceData.cs b/libraries/Microsoft.Bot.Builder.Ai.LUIS/Generator/InstanceData.cs
new file mode 100644
index 0000000000..8a1a55c7ec
--- /dev/null
+++ b/libraries/Microsoft.Bot.Builder.Ai.LUIS/Generator/InstanceData.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using Newtonsoft.Json;
+
+namespace Microsoft.Bot.Builder.Ai.LUIS
+{
+ ///
+ /// Strongly typed information corresponding to LUIS $instance value.
+ ///
+ public class InstanceData
+ {
+ ///
+ /// 0-based index in the analyzed text for where entity starts.
+ ///
+ [JsonProperty("startIndex")]
+ public int StartIndex;
+
+ ///
+ /// 0-based index of the first character beyond the recognized entity.
+ ///
+ [JsonProperty("endIndex")]
+ public int EndIndex;
+
+ ///
+ /// Word broken and normalized text for the entity.
+ ///
+ [JsonProperty("text")]
+ public string Text;
+
+ ///
+ /// Optional confidence in the recognition.
+ ///
+ [JsonProperty("score")]
+ public double? Score;
+ }
+}
diff --git a/libraries/Microsoft.Bot.Builder.Ai.LUIS/Generator/NumberWithUnit.cs b/libraries/Microsoft.Bot.Builder.Ai.LUIS/Generator/NumberWithUnit.cs
new file mode 100644
index 0000000000..1f5c68a743
--- /dev/null
+++ b/libraries/Microsoft.Bot.Builder.Ai.LUIS/Generator/NumberWithUnit.cs
@@ -0,0 +1,71 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Newtonsoft.Json;
+
+namespace Microsoft.Bot.Builder.Ai.LUIS
+{
+ ///
+ /// Strongly typed class for LUIS number and unit entity recognition.
+ ///
+ ///
+ /// Specific subtypes of this class are generated to match the builtin age, currency, dimension and temperature entities.
+ ///
+ public class NumberWithUnit
+ {
+ ///
+ /// Recognized number, or null if unit only.
+ ///
+ [JsonProperty("number")]
+ public readonly double? Number;
+
+ ///
+ /// Normalized recognized unit.
+ ///
+ [JsonProperty("units")]
+ public readonly string Units;
+
+ public NumberWithUnit(double? number, string units)
+ {
+ Number = number;
+ Units = units;
+ }
+ }
+
+ ///
+ /// Strongly typed LUIS builtin_age.
+ ///
+ public class Age: NumberWithUnit
+ {
+ public Age(double number, string units) : base(number, units) { }
+
+ public override string ToString() => $"Age({Number} {Units})";
+ }
+
+ ///
+ /// Strongly typed LUIS builtin_dimension.
+ ///
+ public class Dimension: NumberWithUnit
+ {
+ public Dimension(double number, string units) : base(number, units) { }
+ public override string ToString() => $"Dimension({Number} {Units})";
+ }
+
+ ///
+ /// Strongly typed LUIS builtin_money.
+ ///
+ public class Money : NumberWithUnit
+ {
+ public Money(double number, string units) : base(number, units) { }
+ public override string ToString() => $"Currency({Number} {Units})";
+ }
+
+ ///
+ /// Strongly typed LUIS builtin_temperature.
+ ///
+ public class Temperature : NumberWithUnit
+ {
+ public Temperature(double number, string units) : base(number, units) { }
+ public override string ToString() => $"Temperature({Number} {Units})";
+ }
+}
diff --git a/libraries/Microsoft.Bot.Builder.Ai.LUIS/ILuisRecognizer.cs b/libraries/Microsoft.Bot.Builder.Ai.LUIS/ILuisRecognizer.cs
deleted file mode 100644
index 29afa4984e..0000000000
--- a/libraries/Microsoft.Bot.Builder.Ai.LUIS/ILuisRecognizer.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Cognitive.LUIS.Models;
-
-namespace Microsoft.Bot.Builder.Ai.LUIS
-{
- ///
- ///
- /// A Luis specific interface that extends the generic Recognizer interface
- ///
- internal interface ILuisRecognizer : IRecognizer
- {
- Task<(RecognizerResult recognizerResult, LuisResult luisResult)> CallAndRecognize(string utterance, CancellationToken ct);
- }
-}
diff --git a/libraries/Microsoft.Bot.Builder.Ai.LUIS/IRecognizer.cs b/libraries/Microsoft.Bot.Builder.Ai.LUIS/IRecognizer.cs
deleted file mode 100644
index 2e7dc25678..0000000000
--- a/libraries/Microsoft.Bot.Builder.Ai.LUIS/IRecognizer.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Microsoft.Bot.Builder.Ai.LUIS
-{
- ///
- /// Interface for Recognizers.
- /// This should be moved to the Core Bot Builder Library once it's stable enough
- ///
- public interface IRecognizer
- {
- ///
- /// Runs an utterance through a recognizer and returns the recognizer results
- ///
- /// utterance
- /// cancellation token
- /// Recognizer Results
- Task Recognize(string utterance, CancellationToken ct);
- }
-}
diff --git a/libraries/Microsoft.Bot.Builder.Ai.LUIS/LuisRecognizer.cs b/libraries/Microsoft.Bot.Builder.Ai.LUIS/LuisRecognizer.cs
index 1192f0ace3..aa99c88aed 100644
--- a/libraries/Microsoft.Bot.Builder.Ai.LUIS/LuisRecognizer.cs
+++ b/libraries/Microsoft.Bot.Builder.Ai.LUIS/LuisRecognizer.cs
@@ -10,26 +10,27 @@
using Microsoft.Cognitive.LUIS.Models;
using Newtonsoft.Json.Linq;
using System.Text.RegularExpressions;
+using Microsoft.Bot.Builder.Core.Extensions;
namespace Microsoft.Bot.Builder.Ai.LUIS
{
///
///
- /// Provides a LUIS-based implementation for the interface.
+ /// A LUIS based implementation of IRecognizer.
///
- public class LuisRecognizer : ILuisRecognizer
+ public class LuisRecognizer : IRecognizer
{
private readonly LuisService _luisService;
private readonly ILuisOptions _luisOptions;
private readonly ILuisRecognizerOptions _luisRecognizerOptions;
private const string MetadataKey = "$instance";
- ///
- /// Creates a new object.
- ///
- /// The LUIS model to use to recognize text.
- /// The LUIS recognizer options to use.
- /// The LUIS request options to use.
+ ///
+ /// Creates a new object.
+ ///
+ /// The LUIS model to use to recognize text.
+ /// The LUIS recognizer options to use.
+ /// The LUIS request options to use.
public LuisRecognizer(ILuisModel luisModel, ILuisRecognizerOptions luisRecognizerOptions = null, ILuisOptions options = null)
{
_luisService = new LuisService(luisModel);
@@ -37,26 +38,22 @@ public LuisRecognizer(ILuisModel luisModel, ILuisRecognizerOptions luisRecognize
_luisRecognizerOptions = luisRecognizerOptions ?? new LuisRecognizerOptions { Verbose = true };
}
- ///
- /// Runs an utterance through a LUIS recognizer and returns the recognizer results.
- ///
- /// The utterance.
- /// A cancellation token.
- /// The recognizer results.
+ ///
public async Task Recognize(string utterance, CancellationToken ct)
{
- var result = await CallAndRecognize(utterance, ct).ConfigureAwait(false);
- return result.recognizerResult;
+ return (await RecognizeInternal(utterance, ct).ConfigureAwait(false));
}
- ///
- /// Runs an utterance through a LUIS recognizer and returns the recognizer results.
- ///
- /// The utterance.
- /// A cancellation token.
- /// The recognizer results and LUIS result.
- /// This method adds metadata to the recognizer's results.
- public Task<(RecognizerResult recognizerResult, LuisResult luisResult)> CallAndRecognize(string utterance, CancellationToken ct)
+ ///
+ public async Task Recognize(string utterance, CancellationToken ct)
+ where T : IRecognizerConvert, new()
+ {
+ var result = new T();
+ result.Convert(await RecognizeInternal(utterance, ct).ConfigureAwait(false));
+ return result;
+ }
+
+ private Task RecognizeInternal(string utterance, CancellationToken ct)
{
if (string.IsNullOrEmpty(utterance))
throw new ArgumentNullException(nameof(utterance));
@@ -66,26 +63,18 @@ public async Task Recognize(string utterance, CancellationToke
return Recognize(luisRequest, ct, _luisRecognizerOptions.Verbose);
}
- ///
- /// Runs an utterance through a LUIS recognizer and returns the recognizer results.
- ///
- /// A LUIS request for an utterance.
- /// A cancellation token.
- /// Whether to add metadata to the recognizer's results.
- /// The recognizer results and LUIS result.
- private async Task<(RecognizerResult recognizerResult, LuisResult luisResult)> Recognize(LuisRequest request, CancellationToken ct, bool verbose)
+ private async Task Recognize(LuisRequest request, CancellationToken ct, bool verbose)
{
var luisResult = await _luisService.QueryAsync(request, ct).ConfigureAwait(false);
-
var recognizerResult = new RecognizerResult
{
Text = request.Query,
AlteredText = luisResult.AlteredQuery,
Intents = GetIntents(luisResult),
- Entities = GetEntitiesAndMetadata(luisResult.Entities, luisResult.CompositeEntities, verbose)
+ Entities = ExtractEntitiesAndMetadata(luisResult.Entities, luisResult.CompositeEntities, verbose),
};
-
- return (recognizerResult, luisResult);
+ recognizerResult.Properties.Add("luisResult", luisResult);
+ return recognizerResult;
}
private static JObject GetIntents(LuisResult luisResult)
@@ -95,7 +84,7 @@ private static JObject GetIntents(LuisResult luisResult)
new JObject { [luisResult.TopScoringIntent.Intent] = luisResult.TopScoringIntent.Score ?? 0 };
}
- private static JObject GetEntitiesAndMetadata(IList entities, IList compositeEntities, bool verbose)
+ private static JObject ExtractEntitiesAndMetadata(IList entities, IList compositeEntities, bool verbose)
{
var entitiesAndMetadata = new JObject();
if (verbose)
@@ -117,18 +106,29 @@ private static JObject GetEntitiesAndMetadata(IList entiti
if (compositeEntityTypes.Contains(entity.Type))
continue;
- AddProperty(entitiesAndMetadata, GetNormalizedEntityType(entity), GetEntityValue(entity));
+ AddProperty(entitiesAndMetadata, ExtractNormalizedEntityType(entity), ExtractEntityValue(entity));
if (verbose)
{
- AddProperty((JObject) entitiesAndMetadata[MetadataKey], GetNormalizedEntityType(entity), GetEntityMetadata(entity));
+ AddProperty((JObject)entitiesAndMetadata[MetadataKey], ExtractNormalizedEntityType(entity), ExtractEntityMetadata(entity));
}
}
return entitiesAndMetadata;
}
- private static JToken GetEntityValue(EntityRecommendation entity)
+ private static JToken Number(dynamic value)
+ {
+ if (value == null)
+ {
+ return null;
+ }
+ return long.TryParse((string)value, out var longVal) ?
+ new JValue(longVal) :
+ new JValue(double.Parse((string)value));
+ }
+
+ private static JToken ExtractEntityValue(EntityRecommendation entity)
{
if (entity.Resolution == null)
return entity.Entity;
@@ -138,41 +138,81 @@ private static JToken GetEntityValue(EntityRecommendation entity)
if (entity.Resolution?.Values == null || entity.Resolution.Values.Count == 0)
return JArray.FromObject(entity.Resolution);
- var resolutionValues = (IEnumerable