Vytvoření vlastních výzev ke shromáždění uživatelských vstupů
Článek
PLATÍ PRO: SDK v4
Konverzace mezi robotem a uživatelem často zahrnuje dotaz (vyzvání) uživatele k zadání informací, parsování odpovědi uživatele a následnému jednání s těmito informacemi. Robot by měl sledovat kontext konverzace, aby mohl spravovat své chování a pamatovat si odpovědi na předchozí otázky. Stav robota je informace, které sleduje, aby správně reagoval na příchozí zprávy.
Tip
Knihovna dialogových oken poskytuje předdefinované výzvy, které poskytují více funkcí, které můžou uživatelé používat. Příkladytěchtoch
Poznámka:
Sady SDK služby Bot Framework JavaScript, C# a Python budou nadále podporovány, ale sada Java SDK se vyřazuje s konečnou dlouhodobou podporou končící v listopadu 2023.
Stávající roboti sestavení pomocí sady Java SDK budou i nadále fungovat.
Kód v tomto článku je založený na ukázce Výzvy uživatelům pro vstup. Budete potřebovat kopii ukázky jazyka C#, ukázky JavaScriptu, ukázky Java nebo ukázky Pythonu.
Ukázkový robot se zeptá uživatele na řadu otázek, ověří některé odpovědi a uloží svůj vstup. Následující diagram znázorňuje vztah mezi třídami toku chatbota, profilu uživatele a toku konverzace.
UserProfile Třída pro informace o uživateli, které robot bude shromažďovat.
Třída ConversationFlow , která má řídit stav naší konverzace při shromažďování informací o uživateli.
Vnitřní ConversationFlow.Question výčet pro sledování místa, kde jste v konverzaci.
userProfile Třída pro informace o uživateli, které robot bude shromažďovat.
Třída conversationFlow , která má řídit stav naší konverzace při shromažďování informací o uživateli.
Vnitřní conversationFlow.question výčet pro sledování místa, kde jste v konverzaci.
UserProfile Třída pro informace o uživateli, které robot bude shromažďovat.
Třída ConversationFlow , která má řídit stav naší konverzace při shromažďování informací o uživateli.
Vnitřní ConversationFlow.Question výčet pro sledování místa, kde jste v konverzaci.
UserProfile Třída pro informace o uživateli, které robot bude shromažďovat.
Třída ConversationFlow , která má řídit stav naší konverzace při shromažďování informací o uživateli.
Vnitřní ConversationFlow.Question výčet pro sledování místa, kde jste v konverzaci.
Stav uživatele bude sledovat jméno uživatele, věk a zvolené datum a stav konverzace bude sledovat, co jste naposledy požádali uživatele.
Vzhledem k tomu, že tento robot neplánujete nasadit, nakonfigurujete stav uživatele a konverzace tak, aby používaly úložiště paměti.
Pomocí obslužné rutiny pro otáčení zprávy robota a vlastností stavu konverzace a uživatele můžete spravovat tok konverzace a kolekci vstupů. V robotovi zaznamenáte informace o vlastnosti stavu přijaté během každé iterace obslužné rutiny otáčení zprávy.
Vytvořte objekty stavu uživatele a konverzace při spuštění a spotřebujte je prostřednictvím injektáže závislostí v konstruktoru robota.
Startup.cs
// Create the Bot Adapter with error handling enabled.
services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();
// Create the storage we'll be using for User and Conversation state. (Memory is great for testing purposes.)
services.AddSingleton<IStorage, MemoryStorage>();
// Create the User state.
services.AddSingleton<UserState>();
// Create the Conversation state.
services.AddSingleton<ConversationState>();
Vytvořte objekty stavu uživatele a konverzace v index.js a spotřebujte je v konstruktoru robota.
index.js
// Catch-all for errors.
adapter.onTurnError = async (context, error) => {
// This check writes out errors to console log .vs. app insights.
// NOTE: In production environment, you should consider logging this to Azure
roboti/customPromptBot.js
class CustomPromptBot extends ActivityHandler {
constructor(conversationState, userState) {
super();
// The state management objects for the conversation and user.
this.conversationState = conversationState;
this.userState = userState;
Pomocí instancí ConversationState a UserState poskytovaných kontejnerem Spring vytvořte CustomPromptBot v getBot metodě getBot. Konstruktor CustomPromptBot bude ukládat odkazy na ConversationState a UserState poskytnuté během spuštění.
Application.java
@Bean
public Bot getBot(
ConversationState conversationState,
UserState userState
) {
return new CustomPromptBot(conversationState, userState);
}
CustomPromptBot.java
private final BotState userState;
private final BotState conversationState;
public CustomPromptBot(ConversationState conversationState, UserState userState) {
this.conversationState = conversationState;
this.userState = userState;
Vytvořte objekty stavu uživatele a konverzace v app.py a spotřebujte je v konstruktoru robota.
app.py
CONVERSATION_STATE = ConversationState(MEMORY)
# Create Bot
BOT = CustomPromptBot(CONVERSATION_STATE, USER_STATE)
# Listen for incoming requests on /api/messages.
roboti/custom_prompt_bot.py
class CustomPromptBot(ActivityHandler):
def __init__(self, conversation_state: ConversationState, user_state: UserState):
if conversation_state is None:
raise TypeError(
"[CustomPromptBot]: Missing parameter. conversation_state is required but None was given"
)
if user_state is None:
raise TypeError(
"[CustomPromptBot]: Missing parameter. user_state is required but None was given"
)
self.conversation_state = conversation_state
self.user_state = user_state
Před koncem turnu zavolejte saveChanges , aby se změny stavu v úložišti změnily.
/**
* Override the ActivityHandler.run() method to save state changes after the bot logic completes.
*/
async run(context) {
await super.run(context);
// Save any state changes. The load happened during the execution of the Dialog.
await this.conversationState.saveChanges(context, false);
await this.userState.saveChanges(context, false);
}
Vytvořte přístupové objekty vlastností pro profil uživatele a vlastnosti toku konverzace a potom voláním get načtěte hodnotu vlastnosti ze stavu.
V konstruktoru vytvoříte přístupové objekty vlastností stavu a nastavíte objekty správy stavu (vytvořené výše) pro naši konverzaci.
roboti/custom_prompt_bot.py
async def on_message_activity(self, turn_context: TurnContext):
# Get the state properties from the turn context.
profile = await self.profile_accessor.get(turn_context, UserProfile)
flow = await self.flow_accessor.get(turn_context, ConversationFlow)
Před koncem turnu zavolejte SaveChangesAsync , aby se změny stavu v úložišti změnily.
# Save changes to UserState and ConversationState
await self.conversation_state.save_changes(turn_context)
await self.user_state.save_changes(turn_context)
Obslužná rutina otáčení zprávy
Při zpracování aktivit zpráv obslužná rutina zprávy používá pomocnou metodu ke správě konverzace a vyzvání uživatele. Pomocná metoda je popsaná v následující části.
async def on_message_activity(self, turn_context: TurnContext):
# Get the state properties from the turn context.
profile = await self.profile_accessor.get(turn_context, UserProfile)
flow = await self.flow_accessor.get(turn_context, ConversationFlow)
await self._fill_out_user_profile(flow, profile, turn_context)
# Save changes to UserState and ConversationState
await self.conversation_state.save_changes(turn_context)
await self.user_state.save_changes(turn_context)
Vyplnění profilu uživatele
Robot vyzve uživatele k zadání informací podle toho, na jaké otázce se robot v předchozím kroku zeptal. Vstup se analyzuje pomocí metody ověřování.
Každá metoda ověřování má podobný návrh:
Vrácená hodnota označuje, jestli je vstup platnou odpovědí na tuto otázku.
Pokud ověření projde, vytvoří parsovanou a normalizovanou hodnotu, která se uloží.
Pokud se ověření nezdaří, vytvoří zprávu, se kterou může robot znovu požádat o informace.
Metody ověřování jsou popsány v následující části.
{
var input = turnContext.Activity.Text?.Trim();
string message;
switch (flow.LastQuestionAsked)
{
case ConversationFlow.Question.None:
await turnContext.SendActivityAsync("Let's get started. What is your name?", null, null, cancellationToken);
flow.LastQuestionAsked = ConversationFlow.Question.Name;
break;
case ConversationFlow.Question.Name:
if (ValidateName(input, out var name, out message))
{
profile.Name = name;
await turnContext.SendActivityAsync($"Hi {profile.Name}.", null, null, cancellationToken);
await turnContext.SendActivityAsync("How old are you?", null, null, cancellationToken);
flow.LastQuestionAsked = ConversationFlow.Question.Age;
break;
}
else
{
await turnContext.SendActivityAsync(message ?? "I'm sorry, I didn't understand that.", null, null, cancellationToken);
break;
}
case ConversationFlow.Question.Age:
if (ValidateAge(input, out var age, out message))
{
profile.Age = age;
await turnContext.SendActivityAsync($"I have your age as {profile.Age}.", null, null, cancellationToken);
await turnContext.SendActivityAsync("When is your flight?", null, null, cancellationToken);
flow.LastQuestionAsked = ConversationFlow.Question.Date;
break;
}
else
{
await turnContext.SendActivityAsync(message ?? "I'm sorry, I didn't understand that.", null, null, cancellationToken);
break;
}
case ConversationFlow.Question.Date:
if (ValidateDate(input, out var date, out message))
{
profile.Date = date;
await turnContext.SendActivityAsync($"Your cab ride to the airport is scheduled for {profile.Date}.");
await turnContext.SendActivityAsync($"Thanks for completing the booking {profile.Name}.");
await turnContext.SendActivityAsync($"Type anything to run the bot again.");
flow.LastQuestionAsked = ConversationFlow.Question.None;
profile = new UserProfile();
break;
}
else
{
await turnContext.SendActivityAsync(message ?? "I'm sorry, I didn't understand that.", null, null, cancellationToken);
break;
}
}
}
roboti/customPromptBot.js
// Manages the conversation flow for filling out the user's profile.
static async fillOutUserProfile(flow, profile, turnContext) {
const input = turnContext.activity.text;
let result;
switch (flow.lastQuestionAsked) {
// If we're just starting off, we haven't asked the user for any information yet.
// Ask the user for their name and update the conversation flag.
case question.none:
await turnContext.sendActivity("Let's get started. What is your name?");
flow.lastQuestionAsked = question.name;
break;
// If we last asked for their name, record their response, confirm that we got it.
// Ask them for their age and update the conversation flag.
case question.name:
result = this.validateName(input);
if (result.success) {
profile.name = result.name;
await turnContext.sendActivity(`I have your name as ${ profile.name }.`);
await turnContext.sendActivity('How old are you?');
flow.lastQuestionAsked = question.age;
break;
} else {
// If we couldn't interpret their input, ask them for it again.
// Don't update the conversation flag, so that we repeat this step.
await turnContext.sendActivity(result.message || "I'm sorry, I didn't understand that.");
break;
}
// If we last asked for their age, record their response, confirm that we got it.
// Ask them for their date preference and update the conversation flag.
case question.age:
result = this.validateAge(input);
if (result.success) {
profile.age = result.age;
await turnContext.sendActivity(`I have your age as ${ profile.age }.`);
await turnContext.sendActivity('When is your flight?');
flow.lastQuestionAsked = question.date;
break;
} else {
// If we couldn't interpret their input, ask them for it again.
// Don't update the conversation flag, so that we repeat this step.
await turnContext.sendActivity(result.message || "I'm sorry, I didn't understand that.");
break;
}
// If we last asked for a date, record their response, confirm that we got it,
// let them know the process is complete, and update the conversation flag.
case question.date:
result = this.validateDate(input);
if (result.success) {
profile.date = result.date;
await turnContext.sendActivity(`Your cab ride to the airport is scheduled for ${ profile.date }.`);
await turnContext.sendActivity(`Thanks for completing the booking ${ profile.name }.`);
await turnContext.sendActivity('Type anything to run the bot again.');
flow.lastQuestionAsked = question.none;
profile = {};
break;
} else {
// If we couldn't interpret their input, ask them for it again.
// Don't update the conversation flag, so that we repeat this step.
await turnContext.sendActivity(result.message || "I'm sorry, I didn't understand that.");
break;
}
}
}
CustomPromptBot.java
private static CompletableFuture<Void> fillOutUserProfile(ConversationFlow flow,
UserProfile profile,
TurnContext turnContext) {
String input = "";
if (StringUtils.isNotBlank(turnContext.getActivity().getText())) {
input = turnContext.getActivity().getText().trim();
}
switch (flow.getLastQuestionAsked()) {
case None:
return turnContext.sendActivity("Let's get started. What is your name?", null, null)
.thenRun(() -> {flow.setLastQuestionAsked(ConversationFlow.Question.Name);});
case Name:
Triple<Boolean, String, String> nameValidationResult = validateName(input);
if (nameValidationResult.getLeft()) {
profile.setName(nameValidationResult.getMiddle());
return turnContext.sendActivity(String.format("Hi %s.", profile.getName()), null, null)
.thenCompose(result -> turnContext.sendActivity("How old are you?", null, null))
.thenRun(() -> { flow.setLastQuestionAsked(ConversationFlow.Question.Age); });
} else {
if (StringUtils.isNotBlank(nameValidationResult.getRight())) {
return turnContext.sendActivity(nameValidationResult.getRight(), null, null)
.thenApply(result -> null);
} else {
return turnContext.sendActivity("I'm sorry, I didn't understand that.", null, null)
.thenApply(result -> null);
}
}
case Age:
Triple<Boolean, Integer, String> ageValidationResult = ValidateAge(input);
if (ageValidationResult.getLeft()) {
profile.setAge(ageValidationResult.getMiddle());
return turnContext.sendActivity(String.format("I have your age as %d.", profile.getAge()), null, null)
.thenCompose(result -> turnContext.sendActivity("When is your flight?", null, null))
.thenRun(() -> { flow.setLastQuestionAsked(ConversationFlow.Question.Date); });
} else {
if (StringUtils.isNotBlank(ageValidationResult.getRight())) {
return turnContext.sendActivity(ageValidationResult.getRight(), null, null)
.thenApply(result -> null);
} else {
return turnContext.sendActivity("I'm sorry, I didn't understand that.", null, null)
.thenApply(result -> null);
}
}
case Date:
Triple<Boolean, String, String> dateValidationResult = ValidateDate(input);
AtomicReference<UserProfile> profileReference = new AtomicReference<UserProfile>(profile);
if (dateValidationResult.getLeft()) {
profile.setDate(dateValidationResult.getMiddle());
return turnContext.sendActivity(
String.format("Your cab ride to the airport is scheduled for %s.",
profileReference.get().getDate()))
.thenCompose(result -> turnContext.sendActivity(
String.format("Thanks for completing the booking %s.", profileReference.get().getDate())))
.thenCompose(result -> turnContext.sendActivity("Type anything to run the bot again."))
.thenRun(() -> {
flow.setLastQuestionAsked(ConversationFlow.Question.None);
profileReference.set(new UserProfile());
});
} else {
if (StringUtils.isNotBlank(dateValidationResult.getRight())) {
return turnContext.sendActivity(dateValidationResult.getRight(), null, null)
.thenApply(result -> null);
} else {
return turnContext.sendActivity("I'm sorry, I didn't understand that.", null, null)
.thenApply(result -> null);
}
}
default:
return CompletableFuture.completedFuture(null);
}
roboti/custom_prompt_bot.py
async def _fill_out_user_profile(
self, flow: ConversationFlow, profile: UserProfile, turn_context: TurnContext
):
user_input = turn_context.activity.text.strip()
# ask for name
if flow.last_question_asked == Question.NONE:
await turn_context.send_activity(
MessageFactory.text("Let's get started. What is your name?")
)
flow.last_question_asked = Question.NAME
# validate name then ask for age
elif flow.last_question_asked == Question.NAME:
validate_result = self._validate_name(user_input)
if not validate_result.is_valid:
await turn_context.send_activity(
MessageFactory.text(validate_result.message)
)
else:
profile.name = validate_result.value
await turn_context.send_activity(
MessageFactory.text(f"Hi {profile.name}")
)
await turn_context.send_activity(
MessageFactory.text("How old are you?")
)
flow.last_question_asked = Question.AGE
# validate age then ask for date
elif flow.last_question_asked == Question.AGE:
validate_result = self._validate_age(user_input)
if not validate_result.is_valid:
await turn_context.send_activity(
MessageFactory.text(validate_result.message)
)
else:
profile.age = validate_result.value
await turn_context.send_activity(
MessageFactory.text(f"I have your age as {profile.age}.")
)
await turn_context.send_activity(
MessageFactory.text("When is your flight?")
)
flow.last_question_asked = Question.DATE
# validate date and wrap it up
elif flow.last_question_asked == Question.DATE:
validate_result = self._validate_date(user_input)
if not validate_result.is_valid:
await turn_context.send_activity(
MessageFactory.text(validate_result.message)
)
else:
profile.date = validate_result.value
await turn_context.send_activity(
MessageFactory.text(
f"Your cab ride to the airport is scheduled for {profile.date}."
)
)
await turn_context.send_activity(
MessageFactory.text(
f"Thanks for completing the booking {profile.name}."
)
)
await turn_context.send_activity(
MessageFactory.text("Type anything to run the bot again.")
)
flow.last_question_asked = Question.NONE
Parsování a ověření vstupu
Robot k ověření vstupu používá následující kritéria.
Název musí být neprázdný řetězec. Normalizuje se tím, že oříznou prázdné znaky.
Věk musí být mezi 18 a 120. Normalizuje se vrácením celého čísla.
Datum musí být libovolné datum nebo čas aspoň hodinu v budoucnu.
Normalizuje se vrácením jenom části analyzovaného vstupu s datem.
Poznámka:
Pro vstup stáří a data se v ukázce používají knihovny Microsoft/Recognizers-Text k provedení počáteční analýzy.
Toto je jen jeden způsob, jak analyzovat vstup. Další informace o těchto knihovnách najdete v souboru README projektu.
private static bool ValidateName(string input, out string name, out string message)
{
name = null;
message = null;
if (string.IsNullOrWhiteSpace(input))
{
message = "Please enter a name that contains at least one character.";
}
else
{
name = input.Trim();
}
return message is null;
}
private static bool ValidateAge(string input, out int age, out string message)
{
age = 0;
message = null;
// Try to recognize the input as a number. This works for responses such as "twelve" as well as "12".
try
{
// Attempt to convert the Recognizer result to an integer. This works for "a dozen", "twelve", "12", and so on.
// The recognizer returns a list of potential recognition results, if any.
var results = NumberRecognizer.RecognizeNumber(input, Culture.English);
foreach (var result in results)
{
// The result resolution is a dictionary, where the "value" entry contains the processed string.
if (result.Resolution.TryGetValue("value", out var value))
{
age = Convert.ToInt32(value);
if (age >= 18 && age <= 120)
{
return true;
}
}
}
message = "Please enter an age between 18 and 120.";
}
catch
{
message = "I'm sorry, I could not interpret that as an age. Please enter an age between 18 and 120.";
}
return message is null;
}
private static bool ValidateDate(string input, out string date, out string message)
{
date = null;
message = null;
// Try to recognize the input as a date-time. This works for responses such as "11/14/2018", "9pm", "tomorrow", "Sunday at 5pm", and so on.
// The recognizer returns a list of potential recognition results, if any.
try
{
var results = DateTimeRecognizer.RecognizeDateTime(input, Culture.English);
// Check whether any of the recognized date-times are appropriate,
// and if so, return the first appropriate date-time. We're checking for a value at least an hour in the future.
var earliest = DateTime.Now.AddHours(1.0);
foreach (var result in results)
{
// The result resolution is a dictionary, where the "values" entry contains the processed input.
var resolutions = result.Resolution["values"] as List<Dictionary<string, string>>;
foreach (var resolution in resolutions)
{
// The processed input contains a "value" entry if it is a date-time value, or "start" and
// "end" entries if it is a date-time range.
if (resolution.TryGetValue("value", out var dateString)
|| resolution.TryGetValue("start", out dateString))
{
if (DateTime.TryParse(dateString, out var candidate)
&& earliest < candidate)
{
date = candidate.ToShortDateString();
return true;
}
}
}
}
message = "I'm sorry, please enter a date at least an hour out.";
}
catch
{
message = "I'm sorry, I could not interpret that as an appropriate date. Please enter a date at least an hour out.";
}
return false;
}
roboti/customPromptBot.js
// Validates name input. Returns whether validation succeeded and either the parsed and normalized
// value or a message the bot can use to ask the user again.
static validateName(input) {
const name = input && input.trim();
return name !== undefined
? { success: true, name: name }
: { success: false, message: 'Please enter a name that contains at least one character.' };
};
// Validates age input. Returns whether validation succeeded and either the parsed and normalized
// value or a message the bot can use to ask the user again.
static validateAge(input) {
// Try to recognize the input as a number. This works for responses such as "twelve" as well as "12".
try {
// Attempt to convert the Recognizer result to an integer. This works for "a dozen", "twelve", "12", and so on.
// The recognizer returns a list of potential recognition results, if any.
const results = Recognizers.recognizeNumber(input, Recognizers.Culture.English);
let output;
results.forEach(result => {
// result.resolution is a dictionary, where the "value" entry contains the processed string.
const value = result.resolution.value;
if (value) {
const age = parseInt(value);
if (!isNaN(age) && age >= 18 && age <= 120) {
output = { success: true, age: age };
return;
}
}
});
return output || { success: false, message: 'Please enter an age between 18 and 120.' };
} catch (error) {
return {
success: false,
message: "I'm sorry, I could not interpret that as an age. Please enter an age between 18 and 120."
};
}
}
// Validates date input. Returns whether validation succeeded and either the parsed and normalized
// value or a message the bot can use to ask the user again.
static validateDate(input) {
// Try to recognize the input as a date-time. This works for responses such as "11/14/2018", "today at 9pm", "tomorrow", "Sunday at 5pm", and so on.
// The recognizer returns a list of potential recognition results, if any.
try {
const results = Recognizers.recognizeDateTime(input, Recognizers.Culture.English);
const now = new Date();
const earliest = now.getTime() + (60 * 60 * 1000);
let output;
results.forEach(result => {
// result.resolution is a dictionary, where the "values" entry contains the processed input.
result.resolution.values.forEach(resolution => {
// The processed input contains a "value" entry if it is a date-time value, or "start" and
// "end" entries if it is a date-time range.
const datevalue = resolution.value || resolution.start;
// If only time is given, assume it's for today.
const datetime = resolution.type === 'time'
? new Date(`${ now.toLocaleDateString() } ${ datevalue }`)
: new Date(datevalue);
if (datetime && earliest < datetime.getTime()) {
output = { success: true, date: datetime.toLocaleDateString() };
return;
}
});
});
return output || { success: false, message: "I'm sorry, please enter a date at least an hour out." };
} catch (error) {
return {
success: false,
message: "I'm sorry, I could not interpret that as an appropriate date. Please enter a date at least an hour out."
};
}
}
CustomPromptBot.java
private static Triple<Boolean, String, String> validateName(String input) {
String name = null;
String message = null;
if (StringUtils.isEmpty(input)) {
message = "Please enter a name that contains at least one character.";
} else {
name = input.trim();
}
return Triple.of(StringUtils.isBlank(message), name, message);
}
private static Triple<Boolean, Integer, String> ValidateAge(String input) {
int age = 0;
String message = null;
// Try to recognize the input as a number. This works for responses such as "twelve" as well as "12".
try {
// Attempt to convert the Recognizer result to an integer. This works for "a dozen", "twelve", "12", and so on.
// The recognizer returns a list of potential recognition results, if any.
List<ModelResult> results = NumberRecognizer.recognizeNumber(input, PromptCultureModels.ENGLISH_CULTURE);
for (ModelResult result : results) {
// The result resolution is a dictionary, where the "value" entry contains the processed String.
Object value = result.resolution.get("value");
if (value != null) {
age = Integer.parseInt((String) value);
if (age >= 18 && age <= 120) {
return Triple.of(true, age, "");
}
}
}
message = "Please enter an age between 18 and 120.";
}
catch (Throwable th) {
message = "I'm sorry, I could not interpret that as an age. Please enter an age between 18 and 120.";
}
return Triple.of(StringUtils.isBlank(message), age, message);
}
private static Triple<Boolean, String, String> ValidateDate(String input) {
String date = null;
String message = null;
// Try to recognize the input as a date-time. This works for responses such as "11/14/2018", "9pm", "tomorrow", "Sunday at 5pm", and so on.
// The recognizer returns a list of potential recognition results, if any.
try {
List<ModelResult> results = DateTimeRecognizer.recognizeDateTime(input, PromptCultureModels.ENGLISH_CULTURE);
// Check whether any of the recognized date-times are appropriate,
// and if so, return the first appropriate date-time. We're checking for a value at least an hour in the future.
LocalDateTime earliest = LocalDateTime.now().plus(1, ChronoUnit.HOURS);
for (ModelResult result : results) {
// The result resolution is a dictionary, where the "values" entry contains the processed input.
List<Map<String, Object>> resolutions = (List<Map<String, Object>>) result.resolution.get("values");
for (Map<String, Object> resolution : resolutions) {
// The processed input contains a "value" entry if it is a date-time value, or "start" and
// "end" entries if it is a date-time range.
String dateString = (String) resolution.get("value");
if (StringUtils.isBlank(dateString)) {
dateString = (String) resolution.get("start");
}
if (StringUtils.isNotBlank(dateString)){
DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime candidate;
try {
candidate = LocalDateTime.from(f.parse(dateString));
} catch (DateTimeParseException err) {
// If the input is a date, it will throw an exception and it will create a datetime
// with the MIN localtime
DateTimeFormatter d = DateTimeFormatter.ofPattern("yyyy-MM-dd");
candidate = LocalDateTime.of(LocalDate.parse(dateString, d), LocalDateTime.MIN.toLocalTime());
}
if (earliest.isBefore(candidate)) {
DateTimeFormatter dateformat = DateTimeFormatter.ofPattern("MM-dd-yyyy");
date = candidate.format(dateformat);
return Triple.of(true, date, message);
}
}
}
}
roboti/custom_prompt_bot.py
def _validate_name(self, user_input: str) -> ValidationResult:
if not user_input:
return ValidationResult(
is_valid=False,
message="Please enter a name that contains at least one character.",
)
return ValidationResult(is_valid=True, value=user_input)
def _validate_age(self, user_input: str) -> ValidationResult:
# Attempt to convert the Recognizer result to an integer. This works for "a dozen", "twelve", "12", and so on.
# The recognizer returns a list of potential recognition results, if any.
results = recognize_number(user_input, Culture.English)
for result in results:
if "value" in result.resolution:
age = int(result.resolution["value"])
if 18 <= age <= 120:
return ValidationResult(is_valid=True, value=age)
return ValidationResult(
is_valid=False, message="Please enter an age between 18 and 120."
)
def _validate_date(self, user_input: str) -> ValidationResult:
try:
# Try to recognize the input as a date-time. This works for responses such as "11/14/2018", "9pm",
# "tomorrow", "Sunday at 5pm", and so on. The recognizer returns a list of potential recognition results,
# if any.
results = recognize_datetime(user_input, Culture.English)
for result in results:
for resolution in result.resolution["values"]:
if "value" in resolution:
now = datetime.now()
value = resolution["value"]
if resolution["type"] == "date":
candidate = datetime.strptime(value, "%Y-%m-%d")
elif resolution["type"] == "time":
candidate = datetime.strptime(value, "%H:%M:%S")
candidate = candidate.replace(
year=now.year, month=now.month, day=now.day
)
else:
candidate = datetime.strptime(value, "%Y-%m-%d %H:%M:%S")
# user response must be more than an hour out
diff = candidate - now
if diff.total_seconds() >= 3600:
return ValidationResult(
is_valid=True,
value=candidate.strftime("%m/%d/%y"),
)
return ValidationResult(
is_valid=False,
message="I'm sorry, please enter a date at least an hour out.",
)
except ValueError:
return ValidationResult(
is_valid=False,
message="I'm sorry, I could not interpret that as an appropriate "
"date. Please enter a date at least an hour out.",
)