Merge pull request #188 from Microsoft/CLM/Prompts

Clm/prompts - authored by Tom in an amazing fit of coding frenzy!!
This commit is contained in:
Tom Laird-McConnell 2018-03-01 08:52:19 -08:00 коммит произвёл GitHub
Родитель 9b06f3b071 ff217ca15b
Коммит d28d218f78
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
31 изменённых файлов: 1803 добавлений и 3 удалений

Просмотреть файл

@ -40,6 +40,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Connector.Tes
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Samples.Ai.QnA", "samples\Microsoft.Bot.Samples.Ai.QnA\Microsoft.Bot.Samples.Ai.QnA.csproj", "{A8CE6B0F-E054-45F7-B4D7-23D2C65D2D26}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Bot.Builder.Prompts", "libraries\Microsoft.Bot.Builder.Prompts\Microsoft.Bot.Builder.Prompts.csproj", "{7A197F41-3411-47BF-87A6-5BC8A44CDB74}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Bot.Builder.Prompts.Tests", "tests\Microsoft.Bot.Builder.Prompts.Tests\Microsoft.Bot.Builder.Prompts.Tests.csproj", "{5D145BFC-1C30-4FC5-989A-82260F8225BE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -110,6 +114,14 @@ Global
{A8CE6B0F-E054-45F7-B4D7-23D2C65D2D26}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8CE6B0F-E054-45F7-B4D7-23D2C65D2D26}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A8CE6B0F-E054-45F7-B4D7-23D2C65D2D26}.Release|Any CPU.Build.0 = Release|Any CPU
{7A197F41-3411-47BF-87A6-5BC8A44CDB74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7A197F41-3411-47BF-87A6-5BC8A44CDB74}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7A197F41-3411-47BF-87A6-5BC8A44CDB74}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7A197F41-3411-47BF-87A6-5BC8A44CDB74}.Release|Any CPU.Build.0 = Release|Any CPU
{5D145BFC-1C30-4FC5-989A-82260F8225BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5D145BFC-1C30-4FC5-989A-82260F8225BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5D145BFC-1C30-4FC5-989A-82260F8225BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5D145BFC-1C30-4FC5-989A-82260F8225BE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -131,6 +143,8 @@ Global
{C8DF8AE3-0A4D-445D-993E-F7A1784BFDD4} = {3ADFB27A-95FA-4330-B211-1D66A29A17AB}
{BF414C86-DB3B-4022-9B29-DCE8AA954C12} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{A8CE6B0F-E054-45F7-B4D7-23D2C65D2D26} = {3ADFB27A-95FA-4330-B211-1D66A29A17AB}
{7A197F41-3411-47BF-87A6-5BC8A44CDB74} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{5D145BFC-1C30-4FC5-989A-82260F8225BE} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7173C9F3-A7F9-496E-9078-9156E35D6E16}

Просмотреть файл

@ -12,7 +12,7 @@ namespace Microsoft.Bot.Builder.Adapters
public class TestAdapter : BotAdapter
{
private int _nextId = 0;
private readonly List<Activity> botReplies = new List<Activity>();
private readonly List<Activity> botReplies = new List<Activity>();
public TestAdapter(ConversationReference reference = null)
{
@ -290,7 +290,12 @@ namespace Microsoft.Bot.Builder.Adapters
if (expected.Type != reply.Type)
throw new Exception($"{description}: Type should match");
if (expected.AsMessageActivity().Text != reply.AsMessageActivity().Text)
throw new Exception($"{description}: Text should match");
{
if (description == null)
throw new Exception($"Expected:{expected.AsMessageActivity().Text}\nReceived:{reply.AsMessageActivity().Text}");
else
throw new Exception($"{description}: Text should match");
}
// TODO, expand this to do all properties set on expected
}, description, timeout);
}

Просмотреть файл

@ -0,0 +1,28 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.Recognizers.Text.Number;
using Microsoft.Recognizers.Text.NumberWithUnit;
using static Microsoft.Bot.Builder.Prompts.PromptValidatorEx;
namespace Microsoft.Bot.Builder.Prompts
{
/// <summary>
/// AgePrompt recognizes age expressions like "95 years"
/// </summary>
public class AgePrompt : NumberWithUnitPrompt
{
public AgePrompt(string culture, PromptValidator<NumberWithUnit> validator = null)
: base(NumberWithUnitRecognizer.Instance.GetAgeModel(culture), validator)
{
}
protected AgePrompt(IModel model, PromptValidator<NumberWithUnit> validator = null)
: base(model, validator)
{
}
}
}

Просмотреть файл

@ -0,0 +1,57 @@
using Microsoft.Bot.Schema;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using static Microsoft.Bot.Builder.Prompts.PromptValidatorEx;
namespace Microsoft.Bot.Builder.Prompts
{
public abstract class BasePrompt<T>
{
private readonly PromptValidator<T> _customValidator = null;
public BasePrompt(PromptValidator<T> validator = null)
{
_customValidator = validator;
}
/// <summary>
/// Creates a new Message, and queues it for sending to the user.
/// </summary>
public Task Prompt(IBotContext context, string text, string speak = null)
{
IMessageActivity activity = MessageFactory.Text(text, speak);
activity.InputHint = InputHints.ExpectingInput;
return Prompt(context, activity);
}
/// <summary>
/// Creates a new Message Activity, and queues it for sending to the user.
/// </summary>
public Task Prompt(IBotContext context, IMessageActivity activity)
{
context.Reply(activity);
return Task.CompletedTask;
}
/// <summary>
/// implement to recognize the basic type
/// </summary>
/// <param name="context"></param>
/// <returns>null if not recognized</returns>
public abstract Task<T> Recognize(IBotContext context);
protected virtual Task<bool> Validate(IBotContext context, T value)
{
// Validation passed. Return the validated text.
if (_customValidator != null)
{
return _customValidator(context, value);
}
return Task.FromResult(true);
}
}
}

Просмотреть файл

@ -0,0 +1,28 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.Recognizers.Text.Number;
using Microsoft.Recognizers.Text.NumberWithUnit;
using static Microsoft.Bot.Builder.Prompts.PromptValidatorEx;
namespace Microsoft.Bot.Builder.Prompts
{
/// <summary>
/// CurrencyPrompt recognizes currency expressions as float type
/// </summary>
public class CurrencyPrompt : NumberWithUnitPrompt
{
public CurrencyPrompt(string culture, PromptValidator<NumberWithUnit> validator = null)
: base(NumberWithUnitRecognizer.Instance.GetCurrencyModel(culture), validator)
{
}
protected CurrencyPrompt(IModel model, PromptValidator<NumberWithUnit> validator = null)
: base(model, validator)
{
}
}
}

Просмотреть файл

@ -0,0 +1,82 @@
//using System;
//using System.Linq;
//using System.Threading.Tasks;
//using Microsoft.Bot.Schema;
//using Microsoft.Recognizers.Text;
//using Microsoft.Recognizers.Text.Number;
//using Microsoft.Recognizers.Text.NumberWithUnit;
//using static Microsoft.Bot.Builder.Prompts.PromptValidatorEx;
//namespace Microsoft.Bot.Builder.Prompts
//{
// public class NumberWithUnit
// {
// public NumberWithUnit() { }
// public string Unit { get; set; }
// public float Amount { get; set; }
// public string Text { get; set; }
// }
// /// <summary>
// /// CurrencyPrompt recognizes currency expressions as float type
// /// </summary>
// public class NumberWithUnitPrompt : BasePrompt<NumberWithUnit>
// {
// private readonly PromptValidator<NumberWithUnit, bool> _customValidator = null;
// private IModel _model;
// protected NumberWithUnitPrompt(IModel model, PromptValidator<NumberWithUnit, bool> validator = null)
// {
// _model = model ?? throw new ArgumentNullException(nameof(model));
// _customValidator = validator;
// }
// /// <summary>
// /// Used to validate the incoming text, expected on context.Request, is
// /// valid according to the rules defined in the validation steps.
// /// </summary>
// public override async Task<NumberWithUnit> Recognize(IBotContext context)
// {
// BotAssert.ContextNotNull(context);
// BotAssert.ActivityNotNull(context.Request);
// if (context.Request.Type != ActivityTypes.Message)
// throw new InvalidOperationException("No Message to Recognize");
// IMessageActivity message = context.Request.AsMessageActivity();
// var results = _model.Parse(message.Text);
// if (results.Any())
// {
// var result = results.First();
// NumberWithUnit value = new NumberWithUnit()
// {
// Text = result.Text,
// Unit = (string)result.Resolution["unit"],
// Amount = float.NaN
// };
// if (float.TryParse(result.Resolution["amount"]?.ToString() ?? String.Empty, out float val))
// value.Amount = val;
// if (await Validate(context, value))
// return value;
// }
// return null;
// }
// protected Task<bool> Validate(IBotContext context, NumberWithUnit value)
// {
// // Validation passed. Return the validated text.
// if (_customValidator != null)
// {
// return _customValidator(context, value);
// }
// return Task.FromResult(true);
// }
// }
//}
//}

Просмотреть файл

@ -0,0 +1,28 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.Recognizers.Text.Number;
using Microsoft.Recognizers.Text.NumberWithUnit;
using static Microsoft.Bot.Builder.Prompts.PromptValidatorEx;
namespace Microsoft.Bot.Builder.Prompts
{
/// <summary>
/// DimensionPrompt recognizes dimension expressions like "4 feet" or "6 miles"
/// </summary>
public class DimensionPrompt : NumberWithUnitPrompt
{
public DimensionPrompt(string culture, PromptValidator<NumberWithUnit> validator = null)
: base(NumberWithUnitRecognizer.Instance.GetDimensionModel(culture), validator)
{
}
protected DimensionPrompt(IModel model, PromptValidator<NumberWithUnit> validator = null)
: base(model, validator)
{
}
}
}

Просмотреть файл

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Recognizers.Text.DateTime" Version="1.0.0.36" />
<PackageReference Include="Microsoft.Recognizers.Text.Number" Version="1.0.0.36" />
<PackageReference Include="Microsoft.Recognizers.Text.NumberWithUnit" Version="1.0.0.36" />
<PackageReference Include="Microsoft.Recognizers.Text.Sequence" Version="1.0.0.36" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.Bot.Builder.Core\Microsoft.Bot.Builder.Core.csproj" />
<ProjectReference Include="..\Microsoft.Bot.Schema\Microsoft.Bot.Schema.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,83 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.Recognizers.Text.Number;
using static Microsoft.Bot.Builder.Prompts.PromptValidatorEx;
namespace Microsoft.Bot.Builder.Prompts
{
public class NumberResult<T>
{
public T Value { get; set; }
public string Text { get; set; }
}
/// <summary>
/// NumberPrompt recognizes floats or ints
/// </summary>
public class NumberPrompt<T> : BasePrompt<NumberResult<T>>
{
private IModel _model;
public NumberPrompt(string culture, PromptValidator<NumberResult<T>> validator = null)
: base(validator)
{
_model = NumberRecognizer.Instance.GetNumberModel(culture);
}
protected NumberPrompt(IModel model, PromptValidator<NumberResult<T>> validator = null)
: base(validator)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
}
/// <summary>
/// Used to validate the incoming text, expected on context.Request, is
/// valid according to the rules defined in the validation steps.
/// </summary>
public override async Task<NumberResult<T>> Recognize(IBotContext context)
{
BotAssert.ContextNotNull(context);
BotAssert.ActivityNotNull(context.Request);
if (context.Request.Type != ActivityTypes.Message)
throw new InvalidOperationException("No Message to Recognize");
IMessageActivity message = context.Request.AsMessageActivity();
var results = _model.Parse(message.Text);
if (results.Any())
{
var result = results.First();
if (typeof(T) == typeof(float))
{
if (float.TryParse(result.Resolution["value"].ToString(), out float value))
{
NumberResult<T> numberResult = new NumberResult<T>()
{
Value = (T)(object)value,
Text = result.Text
};
if (await Validate(context, numberResult))
return numberResult;
}
}
else if (typeof(T) == typeof(int))
{
if (int.TryParse(result.Resolution["value"].ToString(), out int value))
{
NumberResult<T> numberResult = new NumberResult<T>()
{
Value = (T)(object)value,
Text = result.Text
};
if (await Validate(context, numberResult))
return numberResult;
}
}
}
return null;
}
}
}

Просмотреть файл

@ -0,0 +1,69 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.Recognizers.Text.Number;
using Microsoft.Recognizers.Text.NumberWithUnit;
using static Microsoft.Bot.Builder.Prompts.PromptValidatorEx;
namespace Microsoft.Bot.Builder.Prompts
{
public class NumberWithUnit
{
public NumberWithUnit() { }
public string Unit { get; set; }
public float Value { get; set; }
public string Text { get; set; }
}
/// <summary>
/// CurrencyPrompt recognizes currency expressions as float type
/// </summary>
public class NumberWithUnitPrompt : BasePrompt<NumberWithUnit>
{
private IModel _model;
protected NumberWithUnitPrompt(IModel model, PromptValidator<NumberWithUnit> validator = null)
:base(validator)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
}
/// <summary>
/// Used to validate the incoming text, expected on context.Request, is
/// valid according to the rules defined in the validation steps.
/// </summary>
public override async Task<NumberWithUnit> Recognize(IBotContext context)
{
BotAssert.ContextNotNull(context);
BotAssert.ActivityNotNull(context.Request);
if (context.Request.Type != ActivityTypes.Message)
throw new InvalidOperationException("No Message to Recognize");
IMessageActivity message = context.Request.AsMessageActivity();
var results = _model.Parse(message.Text);
if (results.Any())
{
var result = results.First();
NumberWithUnit value = new NumberWithUnit()
{
Text = result.Text,
Unit = (string)result.Resolution["unit"],
Value = float.NaN
};
if (float.TryParse(result.Resolution["value"]?.ToString() ?? String.Empty, out float val))
value.Value = val;
if (await Validate(context, value))
return value;
}
return null;
}
}
}

Просмотреть файл

@ -0,0 +1,29 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.Recognizers.Text.Number;
using static Microsoft.Bot.Builder.Prompts.PromptValidatorEx;
namespace Microsoft.Bot.Builder.Prompts
{
/// <summary>
/// OrdinalPrompt recognizes pharses like First, 2nd, third, etc.
/// </summary>
public class OrdinalPrompt : NumberPrompt<int>
{
public OrdinalPrompt(string culture, PromptValidator<NumberResult<int>> validator = null)
: base(NumberRecognizer.Instance.GetOrdinalModel(culture), validator)
{
}
protected OrdinalPrompt(IModel model, PromptValidator<NumberResult<int>> validator = null)
: base(model, validator)
{
}
}
}

Просмотреть файл

@ -0,0 +1,62 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.Recognizers.Text.Number;
using static Microsoft.Bot.Builder.Prompts.PromptValidatorEx;
namespace Microsoft.Bot.Builder.Prompts
{
/// <summary>
/// PercentagePrompt recognizes percentage expressions as float type
/// </summary>
public class PercentagePrompt : BasePrompt<NumberResult<float>>
{
private IModel _model;
public PercentagePrompt(string culture, PromptValidator<NumberResult<float>> validator = null)
: base( validator)
{
_model = NumberRecognizer.Instance.GetPercentageModel(culture);
}
protected PercentagePrompt(IModel model, PromptValidator<NumberResult<float>> validator = null)
: base(validator)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
}
/// <summary>
/// Used to validate the incoming text, expected on context.Request, is
/// valid according to the rules defined in the validation steps.
/// </summary>
public override async Task<NumberResult<float>> Recognize(IBotContext context)
{
BotAssert.ContextNotNull(context);
BotAssert.ActivityNotNull(context.Request);
if (context.Request.Type != ActivityTypes.Message)
throw new InvalidOperationException("No Message to Recognize");
IMessageActivity message = context.Request.AsMessageActivity();
var results = _model.Parse(message.Text);
if (results.Any())
{
var result = results.First();
if (float.TryParse(result.Resolution["value"].ToString().TrimEnd('%'), out float value))
{
NumberResult<float> numberResult = new NumberResult<float>()
{
Value = value,
Text = result.Text
};
if (await Validate(context, numberResult))
return numberResult;
}
}
return null;
}
}
}

Просмотреть файл

@ -0,0 +1,29 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.Recognizers.Text.Number;
using Microsoft.Recognizers.Text.NumberWithUnit;
using Microsoft.Recognizers.Text.Sequence;
using static Microsoft.Bot.Builder.Prompts.PromptValidatorEx;
namespace Microsoft.Bot.Builder.Prompts
{
/// <summary>
/// PhoneNumberPrompt recognizes phone numbers
/// </summary>
public class PhoneNumberPrompt : ValuePrompt
{
public PhoneNumberPrompt(string culture, PromptValidator<ValueResult> validator = null) :
base(SequenceRecognizer.Instance.GetPhoneNumberModel(culture), validator)
{
}
protected PhoneNumberPrompt(IModel model, PromptValidator<ValueResult> validator = null) :
base(model, validator)
{
}
}
}

Просмотреть файл

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.Bot.Builder.Prompts
{
public static class PromptValidatorEx
{
/// <summary>
/// Signature of a handler that can be passed to a prompt to provide additional validation logic
/// or to customize the reply sent to the user when their response is invalid.
/// </summary>
/// <typeparam name="InT">Type of value that will recognized and passed to the validator as input</typeparam>
/// <param name="context">Context for the current turn of conversation.</param>
/// <param name="toValidate"></param>
/// <returns>true or false task</returns>
public delegate Task<bool> PromptValidator<InT>(IBotContext context, InT toValidate);
}
}

Просмотреть файл

@ -0,0 +1,71 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.Recognizers.Text.Number;
using static Microsoft.Bot.Builder.Prompts.PromptValidatorEx;
namespace Microsoft.Bot.Builder.Prompts
{
public class RangeResult
{
public float Start { get; set; }
public float End { get; set; }
public string Text { get; set; }
}
/// <summary>
/// RangeResult recognizes range expressions like "between 2 and 4" -> Start =2 End =4
/// </summary>
public class RangePrompt<T> : BasePrompt<RangeResult>
{
private IModel _model;
public RangePrompt(string culture, PromptValidator<RangeResult> validator = null)
:base(validator)
{
_model = NumberRecognizer.Instance.GetNumberRangeModel(culture);
}
protected RangePrompt(IModel model, PromptValidator<RangeResult> validator = null)
:base(validator)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
}
public override async Task<RangeResult> Recognize(IBotContext context)
{
BotAssert.ContextNotNull(context);
BotAssert.ActivityNotNull(context.Request);
if (context.Request.Type != ActivityTypes.Message)
throw new InvalidOperationException("No Message to Recognize");
IMessageActivity message = context.Request.AsMessageActivity();
var results = _model.Parse(message.Text);
if (results.Any())
{
var result = results.First();
if (result.TypeName == "numberrange")
{
string[] values = result.Resolution["value"].ToString().Trim('(', ')').Split(',');
RangeResult rangeResult = new RangeResult()
{
Text = result.Text
};
if (float.TryParse(values[0], out float startValue) && float.TryParse(values[1], out float endValue))
{
rangeResult.Start = startValue;
rangeResult.End = endValue;
}
if (await Validate(context, rangeResult))
return rangeResult;
}
}
return null;
}
}
}

Просмотреть файл

@ -0,0 +1,28 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.Recognizers.Text.Number;
using Microsoft.Recognizers.Text.NumberWithUnit;
using static Microsoft.Bot.Builder.Prompts.PromptValidatorEx;
namespace Microsoft.Bot.Builder.Prompts
{
/// <summary>
/// TemperaturePrompt recognizes temperature expressions
/// </summary>
public class TemperaturePrompt : NumberWithUnitPrompt
{
public TemperaturePrompt(string culture, PromptValidator<NumberWithUnit> validator = null)
: base(NumberWithUnitRecognizer.Instance.GetTemperatureModel(culture), validator)
{
}
protected TemperaturePrompt(IModel model, PromptValidator<NumberWithUnit> validator = null)
: base(model, validator)
{
}
}
}

Просмотреть файл

@ -0,0 +1,49 @@
using System;
using System.Threading.Tasks;
using Microsoft.Bot.Schema;
using static Microsoft.Bot.Builder.Prompts.PromptValidatorEx;
namespace Microsoft.Bot.Builder.Prompts
{
/// <summary>
/// Text Prompt provides a simple mechanism to send text to a user
/// and validate a response. The default validator passes on any
/// non-whitespace string. That behavior is easily changed by deriving
/// from this class and authoring custom validation behavior.
///
/// For simple validation changes, a PromptValidator may be passed in to the
/// constructor. If the standard validation passes, the custom PromptValidator
/// will be called.
/// </summary>
public class TextPrompt : BasePrompt<string>
{
/// <summary>
/// Creates a new instance of a TextPrompt allowing a custom validator
/// to be specified. The custom validator will ONLY be called if the
/// Validate method on the class first passes.
/// </summary>
public TextPrompt(PromptValidator<string> validator = null)
:base(validator)
{
}
/// <summary>
/// Used to validate the incoming text, expected on context.Request, is
/// valid according to the rules defined in the validation steps.
/// </summary>
public override async Task<string> Recognize(IBotContext context)
{
BotAssert.ContextNotNull(context);
BotAssert.ActivityNotNull(context.Request);
if (context.Request.Type != ActivityTypes.Message)
throw new InvalidOperationException("No Message to Recognize");
IMessageActivity message = context.Request.AsMessageActivity();
if (await Validate(context, message.Text))
return message.Text;
return null;
}
}
}

Просмотреть файл

@ -0,0 +1,62 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.Recognizers.Text.Number;
using Microsoft.Recognizers.Text.NumberWithUnit;
using static Microsoft.Bot.Builder.Prompts.PromptValidatorEx;
namespace Microsoft.Bot.Builder.Prompts
{
public class ValueResult
{
public ValueResult() { }
public string Value { get; set; }
public string Text { get; set; }
}
/// <summary>
/// CurrencyPrompt recognizes currency expressions as float type
/// </summary>
public class ValuePrompt : BasePrompt<ValueResult>
{
private IModel _model;
protected ValuePrompt(IModel model, PromptValidator<ValueResult> validator = null) : base(validator)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
}
/// <summary>
/// Used to validate the incoming text, expected on context.Request, is
/// valid according to the rules defined in the validation steps.
/// </summary>
public override async Task<ValueResult> Recognize(IBotContext context)
{
BotAssert.ContextNotNull(context);
BotAssert.ActivityNotNull(context.Request);
if (context.Request.Type != ActivityTypes.Message)
throw new InvalidOperationException("No Message to Recognize");
IMessageActivity message = context.Request.AsMessageActivity();
var results = _model.Parse(message.Text);
if (results.Any())
{
var result = results.First();
ValueResult value = new ValueResult()
{
Text = result.Text,
Value = (string)result.Resolution["value"]
};
if (await Validate(context, value))
return value;
}
return null;
}
}
}

Просмотреть файл

@ -14,6 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Builder.Ai\Microsoft.Bot.Builder.Ai.csproj" />
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Builder.Core\Microsoft.Bot.Builder.Core.csproj" />
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Builder\Microsoft.Bot.Builder.csproj" />
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Connector\Microsoft.Bot.Connector.csproj" />
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Schema\Microsoft.Bot.Schema.csproj" />

Просмотреть файл

@ -0,0 +1,92 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Adapters;
using Microsoft.Bot.Builder.Middleware;
using Microsoft.Bot.Builder.Storage;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.Bot.Builder.Prompts.Tests
{
[TestClass]
[TestCategory("Prompts")]
[TestCategory("Age Prompts")]
public class AgePromptTests
{
[TestMethod]
public async Task AgePrompt_Test()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var testPrompt = new AgePrompt(Culture.English);
if (!state.InPrompt)
{
state.InPrompt = true;
await testPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await testPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
{
Assert.IsTrue(result.Value != float.NaN);
Assert.IsNotNull(result.Text);
Assert.IsNotNull(result.Unit);
Assert.IsInstanceOfType(result.Value, typeof(float));
context.Reply($"{result.Value} {result.Unit}");
}
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send("test test test")
.AssertReply("null")
.Send("I am 30 years old")
.AssertReply("30 Year")
.StartTest();
}
[TestMethod]
public async Task AgePrompt_Validator()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var numberPrompt = new AgePrompt(Culture.English, async (ctx, result) => result.Value > 10);
if (!state.InPrompt)
{
state.InPrompt = true;
await numberPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await numberPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
context.Reply($"{result.Value} {result.Unit}");
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send(" it is 1 year old")
.AssertReply("null")
.Send(" it is 15 year old")
.AssertReply("15 Year")
.StartTest();
}
}
}

Просмотреть файл

@ -0,0 +1,92 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Adapters;
using Microsoft.Bot.Builder.Middleware;
using Microsoft.Bot.Builder.Storage;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.Bot.Builder.Prompts.Tests
{
[TestClass]
[TestCategory("Prompts")]
[TestCategory("Currency Prompts")]
public class CurrencyPromptTests
{
[TestMethod]
public async Task CurrencyPrompt_Test()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var testPrompt = new CurrencyPrompt(Culture.English);
if (!state.InPrompt)
{
state.InPrompt = true;
await testPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await testPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
{
Assert.IsTrue(result.Value != float.NaN);
Assert.IsNotNull(result.Text);
Assert.IsNotNull(result.Unit);
Assert.IsInstanceOfType(result.Value, typeof(float));
context.Reply($"{result.Value} {result.Unit}");
}
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send("test test test")
.AssertReply("null")
.Send(" I would like $45.50")
.AssertReply("45.5 Dollar")
.StartTest();
}
[TestMethod]
public async Task CurrencyPrompt_Validator()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var numberPrompt = new CurrencyPrompt(Culture.English, async (ctx, result) => result.Value > 10);
if (!state.InPrompt)
{
state.InPrompt = true;
await numberPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await numberPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
context.Reply($"{result.Value} {result.Unit}");
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send(" I would like $1.00")
.AssertReply("null")
.Send(" I would like $45.50")
.AssertReply("45.5 Dollar")
.StartTest();
}
}
}

Просмотреть файл

@ -0,0 +1,94 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Adapters;
using Microsoft.Bot.Builder.Middleware;
using Microsoft.Bot.Builder.Storage;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.Bot.Builder.Prompts.Tests
{
[TestClass]
[TestCategory("Prompts")]
[TestCategory("Dimension Prompts")]
public class DimensionPromptTests
{
[TestMethod]
public async Task DimensionPrompt_Test()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var testPrompt = new DimensionPrompt(Culture.English);
if (!state.InPrompt)
{
state.InPrompt = true;
await testPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await testPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
{
Assert.IsTrue(result.Value != float.NaN);
Assert.IsNotNull(result.Text);
Assert.IsNotNull(result.Unit);
Assert.IsInstanceOfType(result.Value, typeof(float));
context.Reply($"{result.Value} {result.Unit}");
}
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send("test test test")
.AssertReply("null")
.Send("I am 4 feet wide")
.AssertReply("4 Foot")
.Send(" it is 1 foot wide")
.AssertReply("1 Foot")
.StartTest();
}
[TestMethod]
public async Task DimensionPrompt_Validator()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var numberPrompt = new DimensionPrompt(Culture.English, async (ctx, result) => result.Value > 10);
if (!state.InPrompt)
{
state.InPrompt = true;
await numberPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await numberPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
context.Reply($"{result.Value} {result.Unit}");
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send(" it is 1 foot wide")
.AssertReply("null")
.Send(" it is 40 feet wide")
.AssertReply("40 Foot")
.StartTest();
}
}
}

Просмотреть файл

@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;1705;1998</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
<PackageReference Include="Microsoft.Recognizers.Text" Version="1.0.0.36" />
<PackageReference Include="Microsoft.Recognizers.Text.DateTime" Version="1.0.0.36" />
<PackageReference Include="Microsoft.Recognizers.Text.Number" Version="1.0.0.36" />
<PackageReference Include="Microsoft.Recognizers.Text.NumberWithUnit" Version="1.0.0.36" />
<PackageReference Include="Microsoft.Recognizers.Text.Sequence" Version="1.0.0.36" />
<PackageReference Include="MSTest.TestAdapter" Version="1.2.0" />
<PackageReference Include="MSTest.TestFramework" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Builder.Core\Microsoft.Bot.Builder.Core.csproj" />
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Builder.Prompts\Microsoft.Bot.Builder.Prompts.csproj" />
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Builder\Microsoft.Bot.Builder.csproj" />
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Schema\Microsoft.Bot.Schema.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,142 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Adapters;
using Microsoft.Bot.Builder.Middleware;
using Microsoft.Bot.Builder.Storage;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.Bot.Builder.Prompts.Tests
{
public class TestState : StoreItem
{
public bool InPrompt { get; set; } = false;
}
[TestClass]
[TestCategory("Prompts")]
[TestCategory("Number Prompts")]
public class NumberPromptTests
{
[TestMethod]
public async Task NumberPrompt_Float()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var numberPrompt = new NumberPrompt<float>(Culture.English);
if (!state.InPrompt)
{
state.InPrompt = true;
await numberPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await numberPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
{
Assert.IsTrue(result.Value != float.NaN);
Assert.IsNotNull(result.Text);
Assert.IsInstanceOfType(result.Value, typeof(float));
context.Reply(result.Value.ToString());
}
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send("test test test")
.AssertReply("null")
.Send("asdf df 123")
.AssertReply("123")
.Send(" asdf asd 123.43 adsfsdf ")
.AssertReply("123.43")
.StartTest();
}
[TestMethod]
public async Task NumberPrompt_Int()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var numberPrompt = new NumberPrompt<int>(Culture.English);
if (!state.InPrompt)
{
state.InPrompt = true;
await numberPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await numberPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
{
Assert.IsInstanceOfType(result.Value, typeof(int));
Assert.IsNotNull(result.Text);
context.Reply(result.Value.ToString());
}
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send("test test test")
.AssertReply("null")
.Send("asdf df 123")
.AssertReply("123")
.Send(" asdf asd 123.43 adsfsdf ")
.AssertReply("null")
.StartTest();
}
[TestMethod]
public async Task NumberPrompt_Validator()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var numberPrompt = new NumberPrompt<int>(Culture.English, async (ctx, result) => result.Value < 100);
if (!state.InPrompt)
{
state.InPrompt = true;
await numberPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await numberPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
{
Assert.IsInstanceOfType(result.Value, typeof(int));
Assert.IsTrue(result.Value < 100);
Assert.IsNotNull(result.Text);
context.Reply(result.Value.ToString());
}
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send("asdf df 123")
.AssertReply("null")
.Send(" asdf asd 12 adsfsdf ")
.AssertReply("12")
.StartTest();
}
}
}

Просмотреть файл

@ -0,0 +1,96 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Adapters;
using Microsoft.Bot.Builder.Middleware;
using Microsoft.Bot.Builder.Storage;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.Bot.Builder.Prompts.Tests
{
[TestClass]
[TestCategory("Prompts")]
[TestCategory("Ordinal Prompts")]
public class OrdinalPromptTests
{
[TestMethod]
public async Task OrdinalPrompt_Test()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var testPrompt = new OrdinalPrompt(Culture.English);
if (!state.InPrompt)
{
state.InPrompt = true;
await testPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await testPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
{
Assert.IsTrue(result.Value != float.NaN);
Assert.IsNotNull(result.Text);
Assert.IsInstanceOfType(result.Value, typeof(int));
context.Reply(result.Value.ToString());
}
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send("test test test")
.AssertReply("null")
.Send(" the second one please ")
.AssertReply("2")
.StartTest();
}
[TestMethod]
public async Task OrdinalPrompt_Validator()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var numberPrompt = new OrdinalPrompt(Culture.English, async (ctx, result) => result.Value > 2);
if (!state.InPrompt)
{
state.InPrompt = true;
await numberPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await numberPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
{
Assert.IsInstanceOfType(result.Value, typeof(int));
Assert.IsTrue(result.Value < 100);
Assert.IsNotNull(result.Text);
context.Reply(result.Value.ToString());
}
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send("the first one")
.AssertReply("null")
.Send("the third one")
.AssertReply("3")
.StartTest();
}
}
}

Просмотреть файл

@ -0,0 +1,93 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Adapters;
using Microsoft.Bot.Builder.Middleware;
using Microsoft.Bot.Builder.Storage;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.Bot.Builder.Prompts.Tests
{
[TestClass]
[TestCategory("Prompts")]
[TestCategory("Percentage Prompts")]
public class PercentagePromptTests
{
[TestMethod]
public async Task PercentagePrompt_Test()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var testPrompt = new PercentagePrompt(Culture.English);
if (!state.InPrompt)
{
state.InPrompt = true;
await testPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await testPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
{
Assert.IsTrue(result.Value != float.NaN);
Assert.IsNotNull(result.Text);
Assert.IsInstanceOfType(result.Value, typeof(float));
context.Reply($"{result.Value}");
}
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send("test test test")
.AssertReply("null")
.Send("give me 5")
.AssertReply("null")
.Send(" I would like forty five percent")
.AssertReply("45")
.StartTest();
}
[TestMethod]
public async Task PercentagePrompt_Validator()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var numberPrompt = new PercentagePrompt(Culture.English, async (ctx, result) => result.Value > 10);
if (!state.InPrompt)
{
state.InPrompt = true;
await numberPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await numberPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
context.Reply($"{result.Value}");
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send(" I would like 5%")
.AssertReply("null")
.Send(" I would like 30%")
.AssertReply("30")
.StartTest();
}
}
}

Просмотреть файл

@ -0,0 +1,92 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Adapters;
using Microsoft.Bot.Builder.Middleware;
using Microsoft.Bot.Builder.Storage;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.Bot.Builder.Prompts.Tests
{
[TestClass]
[TestCategory("Prompts")]
[TestCategory("PhoneNumber Prompts")]
public class PhoneNumberPromptTests
{
[TestMethod]
public async Task PhoneNumberPrompt_Test()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var testPrompt = new PhoneNumberPrompt(Culture.English);
if (!state.InPrompt)
{
state.InPrompt = true;
await testPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await testPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
{
Assert.IsNotNull(result.Text);
Assert.IsNotNull(result.Value);
context.Reply($"{result.Value}");
}
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send("test test test")
.AssertReply("null")
.Send("123 123123sdfsdf 123 1asdf23123 123 ")
.AssertReply("null")
.Send("123-456-7890")
.AssertReply("123-456-7890")
.StartTest();
}
[TestMethod]
public async Task PhoneNumberPrompt_Validator()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var numberPrompt = new PhoneNumberPrompt(Culture.English, async (ctx, result) => result.Value.StartsWith("123"));
if (!state.InPrompt)
{
state.InPrompt = true;
await numberPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await numberPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
context.Reply($"{result.Value}");
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send("888-123-4567")
.AssertReply("null")
.Send("123-123-4567")
.AssertReply("123-123-4567")
.StartTest();
}
}
}

Просмотреть файл

@ -0,0 +1,98 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Adapters;
using Microsoft.Bot.Builder.Middleware;
using Microsoft.Bot.Builder.Storage;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.Bot.Builder.Prompts.Tests
{
[TestClass]
[TestCategory("Prompts")]
[TestCategory("Range Prompts")]
public class RangePromptTests
{
[TestMethod]
public async Task RangePrompt_Test()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var testPrompt = new RangePrompt<int>(Culture.English);
if (!state.InPrompt)
{
state.InPrompt = true;
await testPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await testPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
{
Assert.IsTrue(result.Start > 0);
Assert.IsTrue(result.End > result.Start);
Assert.IsNotNull(result.Text);
context.Reply($"{result.Start}-{result.End}");
}
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send("test test test")
.AssertReply("null")
.Send("give me 5 10")
.AssertReply("null")
.Send(" give me between 5 and 10")
.AssertReply("5-10")
.StartTest();
}
[TestMethod]
public async Task RangePrompt_Validator()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var testPrompt = new RangePrompt<int>(Culture.English, async (c, result) => result.End - result.Start > 5);
if (!state.InPrompt)
{
state.InPrompt = true;
await testPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await testPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
{
Assert.IsTrue(result.Start > 0);
Assert.IsTrue(result.End > result.Start);
Assert.IsNotNull(result.Text);
context.Reply($"{result.Start}-{result.End}");
}
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send("give me between 1 and 4")
.AssertReply("null")
.Send(" give me between 1 and 10")
.AssertReply("1-10")
.StartTest();
}
}
}

Просмотреть файл

@ -0,0 +1,92 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Adapters;
using Microsoft.Bot.Builder.Middleware;
using Microsoft.Bot.Builder.Storage;
using Microsoft.Bot.Schema;
using Microsoft.Recognizers.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.Bot.Builder.Prompts.Tests
{
[TestClass]
[TestCategory("Prompts")]
[TestCategory("Temperature Prompts")]
public class TemperaturePromptTests
{
[TestMethod]
public async Task TemperaturePrompt_Test()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var testPrompt = new TemperaturePrompt(Culture.English);
if (!state.InPrompt)
{
state.InPrompt = true;
await testPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await testPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
{
Assert.IsTrue(result.Value != float.NaN);
Assert.IsNotNull(result.Text);
Assert.IsNotNull(result.Unit);
Assert.IsInstanceOfType(result.Value, typeof(float));
context.Reply($"{result.Value} {result.Unit}");
}
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send("test test test")
.AssertReply("null")
.Send(" it is 43 degrees")
.AssertReply("43 Degree")
.StartTest();
}
[TestMethod]
public async Task TemperaturePrompt_Validator()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<TestState>(new MemoryStorage()));
await new TestFlow(adapter, async (context) =>
{
var state = ConversationState<TestState>.Get(context);
var numberPrompt = new TemperaturePrompt(Culture.English, async (ctx, result) => result.Value > 10);
if (!state.InPrompt)
{
state.InPrompt = true;
await numberPrompt.Prompt(context, "Gimme:");
}
else
{
var result = await numberPrompt.Recognize(context);
if (result == null)
context.Reply("null");
else
context.Reply($"{result.Value} {result.Unit}");
}
})
.Send("hello")
.AssertReply("Gimme:")
.Send(" it is 10 degrees")
.AssertReply("null")
.Send(" it is 43 degrees")
.AssertReply("43 Degree")
.StartTest();
}
}
}

Просмотреть файл

@ -0,0 +1,115 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Adapters;
using Microsoft.Bot.Builder.Middleware;
using Microsoft.Bot.Builder.Storage;
using Microsoft.Bot.Schema;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.Bot.Builder.Prompts.Tests
{
[TestClass]
[TestCategory("Prompts")]
[TestCategory("Text Prompts")]
public class TextPromptTests
{
[TestMethod]
public async Task SimpleRecognize()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<StoreItem>(new MemoryStorage()));
await new TestFlow(adapter, MyTestPrompt)
.Send("hello")
.AssertReply("Your Name:")
.Send("test test test")
.AssertReply("Passed")
.AssertReply("test test test")
.StartTest();
}
[TestMethod]
public async Task MinLenghtViaCustomValidator_Fail()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<StoreItem>(new MemoryStorage()));
await new TestFlow(adapter, LengthCheckPromptTest)
.Send("hello")
.AssertReply("Your Name:")
.Send("1")
.AssertReply("Failed")
.StartTest();
}
[TestMethod]
public async Task MinLenghtViaCustomValidator_Pass()
{
TestAdapter adapter = new TestAdapter()
.Use(new ConversationState<StoreItem>(new MemoryStorage()));
await new TestFlow(adapter, LengthCheckPromptTest)
.Send("hello")
.AssertReply("Your Name:")
.Send("123456")
.AssertReply("Passed")
.AssertReply("123456")
.StartTest();
}
public async Task MyTestPrompt(IBotContext context)
{
dynamic conversationState = ConversationState<StoreItem>.Get(context);
TextPrompt askForName = new TextPrompt();
if (conversationState["topic"] != "textPromptTest")
{
conversationState["topic"] = "textPromptTest";
await askForName.Prompt(context, "Your Name:");
}
else
{
var text = await askForName.Recognize(context);
if (text != null)
{
context.Reply("Passed");
context.Reply(text);
}
else
{
context.Reply("Failed");
}
}
}
public async Task LengthCheckPromptTest(IBotContext context)
{
dynamic conversationState = ConversationState<StoreItem>.Get(context);
TextPrompt askForName = new TextPrompt(MinLengthValidator);
if (conversationState["topic"] != "textPromptTest")
{
conversationState["topic"] = "textPromptTest";
await askForName.Prompt(context, "Your Name:");
}
else
{
var text = await askForName.Recognize(context);
if (text != null)
{
context.Reply("Passed");
context.Reply(text);
}
else
{
context.Reply("Failed");
}
}
}
public async Task<bool> MinLengthValidator(IBotContext context, string toValidate)
{
return toValidate.Length > 5;
}
}
}

Просмотреть файл

@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
@ -19,6 +18,7 @@
<ItemGroup>
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Builder.Core\Microsoft.Bot.Builder.Core.csproj" />
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Builder\Microsoft.Bot.Builder.csproj" />
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Schema\Microsoft.Bot.Schema.csproj" />
</ItemGroup>