Issue with more than one LUIS Dialog and its entities - c#

I'm try to create a bot with multiple Luis intents, it means multiple dialogs naturally. I've installed the default CoreBot template (Downloaded from Azure). After setting intents and entities;
created second dialog which name is 'WeatherDialog',
created "WeatherDetails" which consists of getter and setter for my LUIS weather entities,
implemented some of code for accessing to entities' results to partial class which is already in the project for BookingDialog.
Then in the MainDialog, I tried to AddDialog.
MainDialog:
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Logging;
using Microsoft.Recognizers.Text.DataTypes.TimexExpression;
using CoreBot;
namespace Microsoft.BotBuilderSamples.Dialogs
{
public class MainDialog : ComponentDialog
{
private readonly FlightBookingRecognizer _luisRecognizer;
protected readonly ILogger Logger;
// Dependency injection uses this constructor to instantiate MainDialog
public MainDialog(FlightBookingRecognizer luisRecognizer, BookingDialog bookingDialog, CoreBot.Dialogs.WeatherDialog weatherDialog, ILogger<MainDialog> logger)
: base(nameof(MainDialog))
{
_luisRecognizer = luisRecognizer;
Logger = logger;
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(bookingDialog);
AddDialog(weatherDialog);
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
IntroStepAsync,
ActStepAsync,
FinalStepAsync,
}));
// The initial child Dialog to run.
InitialDialogId = nameof(WaterfallDialog);
}
private async Task<DialogTurnResult> IntroStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if (!_luisRecognizer.IsConfigured)
{
await stepContext.Context.SendActivityAsync(
MessageFactory.Text("NOTE: LUIS is not configured. To enable all capabilities, add 'LuisAppId', 'LuisAPIKey' and 'LuisAPIHostName' to the appsettings.json file.", inputHint: InputHints.IgnoringInput), cancellationToken);
return await stepContext.NextAsync(null, cancellationToken);
}
// Use the text provided in FinalStepAsync or the default if it is the first time.
var messageText = stepContext.Options?.ToString() ?? "How can I help you?";
var promptMessage = MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput);
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = promptMessage }, cancellationToken);
}
private async Task<DialogTurnResult> ActStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if (!_luisRecognizer.IsConfigured)
{
// LUIS is not configured, we just run the BookingDialog path with an empty BookingDetailsInstance.
return await stepContext.BeginDialogAsync(nameof(BookingDialog), new BookingDetails(), cancellationToken);
}
// Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt.)
var luisResult = await _luisRecognizer.RecognizeAsync<CoreBot.WeatherModel>(stepContext.Context, cancellationToken);
switch (luisResult.TopIntent().intent)
{
case CoreBot.WeatherModel.Intent.BookFlight:
//await ShowWarningForUnsupportedCities(stepContext.Context, luisResult, cancellationToken);
Console.WriteLine("This is bookflight");
// Initialize BookingDetails with any entities we may have found in the response.
var bookingDetails = new BookingDetails()
{
// Get destination and origin from the composite entities arrays.
Destination = luisResult.ToEntities.Airport,
Origin = luisResult.FromEntities.Airport,
TravelDate = luisResult.TravelDate,
};
// Run the BookingDialog giving it whatever details we have from the LUIS call, it will fill out the remainder.
return await stepContext.BeginDialogAsync(nameof(BookingDialog), bookingDetails, cancellationToken);
case CoreBot.WeatherModel.Intent.GetWeather:
Console.WriteLine("This is getweather");
var weatherDetails = new CoreBot.WeatherDetails()
{
Location = luisResult.Location,
TravelDate = luisResult.TravelDate,
};
return await stepContext.BeginDialogAsync(nameof(CoreBot.Dialogs.WeatherDialog), weatherDetails, cancellationToken);
default:
// Catch all for unhandled intents
var didntUnderstandMessageText = $"Sorry, I didn't get that. Please try asking in a different way (intent was {luisResult.TopIntent().intent})";
var didntUnderstandMessage = MessageFactory.Text(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IgnoringInput);
await stepContext.Context.SendActivityAsync(didntUnderstandMessage, cancellationToken);
break;
}
return await stepContext.NextAsync(null, cancellationToken);
}
// Shows a warning if the requested From or To cities are recognized as entities but they are not in the Airport entity list.
// In some cases LUIS will recognize the From and To composite entities as a valid cities but the From and To Airport values
// will be empty if those entity values can't be mapped to a canonical item in the Airport.
private static async Task ShowWarningForUnsupportedCities(ITurnContext context, FlightBooking luisResult, CancellationToken cancellationToken)
{
var unsupportedCities = new List<string>();
var fromEntities = luisResult.FromEntities;
if (!string.IsNullOrEmpty(fromEntities.From) && string.IsNullOrEmpty(fromEntities.Airport))
{
unsupportedCities.Add(fromEntities.From);
}
var toEntities = luisResult.ToEntities;
if (!string.IsNullOrEmpty(toEntities.To) && string.IsNullOrEmpty(toEntities.Airport))
{
unsupportedCities.Add(toEntities.To);
}
if (unsupportedCities.Any())
{
var messageText = $"Sorry but the following airports are not supported: {string.Join(',', unsupportedCities)}";
var message = MessageFactory.Text(messageText, messageText, InputHints.IgnoringInput);
await context.SendActivityAsync(message, cancellationToken);
}
}
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
// If the child dialog ("BookingDialog") was cancelled, the user failed to confirm or if the intent wasn't BookFlight
// the Result here will be null.
if (stepContext.Result is BookingDetails result)
{
// Now we have all the booking details call the booking service.
// If the call to the booking service was successful tell the user.
var timeProperty = new TimexProperty(result.TravelDate);
var travelDateMsg = timeProperty.ToNaturalLanguage(DateTime.Now);
var messageText = $"I have you booked to {result.Destination} from {result.Origin} on {travelDateMsg}";
var message = MessageFactory.Text(messageText, messageText, InputHints.IgnoringInput);
await stepContext.Context.SendActivityAsync(message, cancellationToken);
}
else if (stepContext.Result is CoreBot.WeatherDetails sonuc)
{
var timeProperty = new TimexProperty(sonuc.TravelDate);
var weatherDateMsg = timeProperty.ToNaturalLanguage(DateTime.Now);
var messageText = $"Thats your weather result for {weatherDateMsg} at {sonuc.Location}: 00 F";
var message = MessageFactory.Text(messageText, messageText, InputHints.IgnoringInput);
await stepContext.Context.SendActivityAsync(message, cancellationToken);
}
// Restart the main dialog with a different message the second time around
var promptMessage = "What else can I do for you?";
return await stepContext.ReplaceDialogAsync(InitialDialogId, promptMessage, cancellationToken);
}
}
}
And also my WeatherDialog:
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
using Microsoft.BotBuilderSamples.Dialogs;
using Microsoft.Recognizers.Text.DataTypes.TimexExpression;
namespace CoreBot.Dialogs
{
public class WeatherDialog : CancelAndHelpDialog
{
private const string DestinationStepMsgText = "Type the location.";
public WeatherDialog()
: base(nameof(WeatherDialog))
{
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));
AddDialog(new DateResolverDialog());
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
LocationStepAsync,
TravelDateStepAsync,
ConfirmStepAsync,
FinalStepAsync,
}));
// The initial child Dialog to run.
InitialDialogId = nameof(WaterfallDialog);
}
private async Task<DialogTurnResult> LocationStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var weatherDetails = (WeatherDetails)stepContext.Options;
if (weatherDetails.Location == null)
{
var promptMessage = MessageFactory.Text(DestinationStepMsgText, DestinationStepMsgText, InputHints.ExpectingInput);
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = promptMessage }, cancellationToken);
}
return await stepContext.NextAsync(weatherDetails.Location, cancellationToken);
}
private async Task<DialogTurnResult> TravelDateStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var weatherDetails = (WeatherDetails)stepContext.Options;
weatherDetails.Location = (string)stepContext.Result;
if (weatherDetails.TravelDate == null || IsAmbiguous(weatherDetails.TravelDate))
{
return await stepContext.BeginDialogAsync(nameof(DateResolverDialog), weatherDetails.TravelDate, cancellationToken);
}
return await stepContext.NextAsync(weatherDetails.TravelDate, cancellationToken);
}
private async Task<DialogTurnResult> ConfirmStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var weatherDetails = (WeatherDetails)stepContext.Options;
weatherDetails.TravelDate = (string)stepContext.Result;
var messageText = $"You want to know weather status at {weatherDetails.Location} for {weatherDetails.TravelDate}. Is this correct?";
var promptMessage = MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput);
return await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions { Prompt = promptMessage }, cancellationToken);
}
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if ((bool)stepContext.Result)
{
var weatherDetails = (WeatherDetails)stepContext.Options;
return await stepContext.EndDialogAsync(weatherDetails, cancellationToken);
}
return await stepContext.EndDialogAsync(null, cancellationToken);
}
private static bool IsAmbiguous(string timex)
{
var timexProperty = new TimexProperty(timex);
return !timexProperty.Types.Contains(Constants.TimexTypes.Definite);
}
}
}
After I build the code from Visual Studio then start conversation from Bot Framework Emulator.
And the following dialog goes like this:
How is the weather today?
+The bot encounted an error or bug. -> Failed
+To continue to run this bot, please fix the bot source code.
How is the weather today at Los Angeles?
-> Successful
How is the weather at Los Angeles?
+The bot encounted an error or bug. -> Failed
+To continue to run this bot, please fix the bot source code.
book me flight
-> Successful
How is the weather tomorrow at Tokyo?
-> Successful ............
Briefly, when running the bookingDialog with missing parameters (entities), bot asks the missing parameter. However, when I run the weatherDialog with missing parameters conversation overs with error.
When I look the LUIS trace in Bot Framework Emulator, intent and entity recognized successfully.
As you can see, bookingDialog works like a charm but my dialog doesn't work. Can you please help me? What am I missing?

Related

How can I get directly response initial dialog in chat bot developed by Microsoft bot frame work

I’m now developing chat bot that reply the question the customer asked. I want to delete the first communication line “Type some words for reply the question”. However, I can’t find the way.
Initialize the communication.
if (member.Id != turnContext.Activity.Recipient.Id)
{
var reply = MessageFactory.Text("質問を続けるには何か入力してください。");
await turnContext.SendActivityAsync(reply, cancellationToken);
}
Ask a customer for a question.
private async Task<DialogTurnResult> InputQuestionAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
_logger.LogInformation("MainDialog.InputQuestionAsync");
var promptOptions = new PromptOptions { Prompt = MessageFactory.Text("ご質問を話し言葉で入力して下さい。") };
return await stepContext.PromptAsync(nameof(TextPrompt), promptOptions, cancellationToken);
}
Reply the question.
private async Task<DialogTurnResult> ShowCardStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
_logger.LogInformation("MainDialog.ShowCardStepAsync");
var receivedMessage = (string)stepContext.Result;
var userProfile = await _userProfileAccessor.GetAsync(stepContext.Context, () => new UserProfile(), cancellationToken);
userProfile.Question = receivedMessage;
*
*
*
*
msg = msg + sec.ToString() + ".知りたい質問がありません\n\n" + third.ToString() + ".質問を変える\n\n";
var reply = MessageFactory.Text(msg);
list1.Add(sec.ToString());
list1.Add(third.ToString());
reply.Attachments.Add(Cards.GetHeroCard(list1).ToAttachment());
await stepContext.Context.SendActivityAsync(reply, cancellationToken);
var promptOptions = new PromptOptions { Prompt = MessageFactory.Text("番号を押してください") };
return await stepContext.PromptAsync(nameof(TextPrompt), promptOptions, cancellationToken);
}
}
[![image of the dialog flow][1]][1]
Add
var accessor = ConversationState.CreateProperty<DialogState>(nameof(DialogState));
await Dialog.RunAsync(turnContext, accessor, cancellationToken);
In
OnMembersAddedAsync and OnMessageActivityAsync solved this issue.

How call a step of waterfall on a particular condition?

I have a bot made using c# and v4 framework. It always asks user do they want to ask more questions from bot with a yes/no prompt:
i want that on "clicking on a particular condition of waterfall should be called" otherwise on all other conditions that step of waterfall should skipped and bot should function normally.I am also adding code just for reference.
The code of main dialog
public MainDialog(ILogger<MainDialog> logger, IBotServices botServices, UserState userState) :
base(nameof(MainDialog))
{ _botServices = botServices;// ?? throw new System.ArgumentNullException(nameof(botServices));
_logger = logger;
_userState = userState;
AddDialog(new ProductIssue($"{nameof(MainDialog)}.fromMain", _botServices, _userState));
AddDialog(new ProductIssue($"{nameof(Confirm)}.fromConfirm", _botServices, _userState));
AddDialog(new ProductIssue($"{ nameof(Resolution)}.resolution", _botServices, _userState));
AddDialog(new PurchaseFlow(_userState));
AddDialog(new Resolution( _userState));
AddDialog(new Confirm(_botServices,_userState));
AddDialog(new ChoicePrompt($"{nameof(MainDialog)}.issue"));
AddDialog(new TextPrompt($"{nameof(MainDialog)}.callDialog"));
AddDialog(new TextPrompt($"{nameof(MainDialog)}.number", ValidationAsync));
AddDialog(new WaterfallDialog($"{nameof(MainDialog)}.mainFlow", new WaterfallStep[]
{
MoblieNumberAsync,
ChoiceCardStepAsync,
ShowCardStepAsync,
CallingDialogsAsync
}));
AddDialog(new TextPrompt(nameof(TextPrompt)));
InitialDialogId = $"{nameof(MainDialog)}.mainFlow";
}
private async Task<DialogTurnResult> MoblieNumberAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
stepContext.Values[UserInfo] = new UserInput();
var options = new PromptOptions()
{
Prompt = MessageFactory.Text("Kindly enter your 10 digit mobile number without any spaces, dashes and country code. We will be sending an OTP later to this number "),
RetryPrompt = MessageFactory.Text("Incorrect mobile number entered. Please only enter the 10 digits of your mobile without any spaces, dashes and country code.")
};
return await stepContext.PromptAsync($"{nameof(MainDialog)}.number", options, cancellationToken);
}
private async Task<DialogTurnResult> ChoiceCardStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var userStateAccessors = _userState.CreateProperty<UserInput>(nameof(UserInput));
var userinfo = await userStateAccessors.GetAsync(stepContext.Context, () => new UserInput());
userinfo.phone_no = (string)stepContext.Result;
CustomerDetails customerDetails = new CustomerDetails();
//API-Get Customer Details from CRM
try
{
BotAPIBLL botApiBLL = new BotAPIBLL();
var response = botApiBLL.GetCustomerDetail(stepContext.Context.Activity.Text);
customerDetails = JsonConvert.DeserializeObject<CustomerDetails>(response);
}
catch(Exception ex)
{
}
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Fetching your details from our systems. This may take a moment"), cancellationToken);
if (customerDetails.D != null && !string.IsNullOrEmpty(customerDetails.D.TelNumber) && !string.IsNullOrEmpty(customerDetails.D.NameFirst))
{
DbConnection dbConnection = new DbConnection();
dbConnection.SaveCutomerInfo(customerDetails);
userinfo.Name = customerDetails.D.NameFirst;
var options = new PromptOptions()
{
Prompt = MessageFactory.Text("Welcome " + customerDetails.D.NameFirst + ", How can we serve you ? "),
RetryPrompt = MessageFactory.Text("That was not a valid choice, please select a option between 1 to 5."),
Choices = GetChoices(),
Style = ListStyle.HeroCard
};
return await stepContext.PromptAsync($"{nameof(MainDialog)}.issue", options, cancellationToken);
}
else
{
var options = new PromptOptions()
{
Prompt = MessageFactory.Text("Welcome Guest_" + userinfo.phone_no + ", How can we serve you ? "),
RetryPrompt = MessageFactory.Text("That was not a valid choice, please select a option between 1 to 5."),
Choices = GetChoices(),
Style = ListStyle.HeroCard
};
return await stepContext.PromptAsync($"{nameof(MainDialog)}.issue", options, cancellationToken);
}
}
private async Task<DialogTurnResult> ShowCardStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var userinfo = (UserInput)stepContext.Values[UserInfo];
userinfo.choiceselected = ((FoundChoice)stepContext.Result).Value;
var attachments = new List<Attachment>();
var reply = MessageFactory.Text("");
var user_choice = ((FoundChoice)stepContext.Result).Value;
switch (user_choice)
{
case "Product issue":
reply.Attachments.Add(Cards.CreateAdaptiveCardAttachment3());
break;
case "Register Product":
reply.Attachments.Add(Cards.GetHeroCard1().ToAttachment());
break;
case "Online Purchase":
return await stepContext.BeginDialogAsync(nameof(PurchaseFlow), null, cancellationToken);
case "Customer Grivance":
reply.Attachments.Add(Cards.GetHeroCard3().ToAttachment());
break;
case "Order Status":
reply.Attachments.Add(Cards.GetHeroCard6().ToAttachment());
break;
default:
reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;
reply.Attachments.Add(Cards.CreateAdaptiveCardAttachment3());
reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;
reply.Attachments.Add(Cards.GetHeroCard1().ToAttachment());
break;
}
if (user_choice == "Register Product" || user_choice == "Online Purchase" || user_choice == "Customer Grivance")
{
await stepContext.Context.SendActivityAsync(reply, cancellationToken);
Random r = new Random();
var validationcard = Cards.CreateAdaptiveCardAttachment2(_cards[r.Next(_cards.Length)]);
//var feedbackcard = Cards.CustomerFeedback();
await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(validationcard), cancellationToken);
//await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(feedbackcard), cancellationToken);
var accessor = _userState.CreateProperty<UserInput>(nameof(UserInput));
await accessor.SetAsync(stepContext.Context, userinfo, cancellationToken);
return await stepContext.EndDialogAsync(null, cancellationToken);
}
else
{
var options2 = new PromptOptions() { Prompt = reply, RetryPrompt = MessageFactory.Text("Retry") };
return await stepContext.PromptAsync($"{nameof(MainDialog)}.callDialog", options2, cancellationToken);
}
}
private async Task<DialogTurnResult> CallingDialogsAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
// First, we use the dispatch model to determine which cognitive service (LUIS or QnA) to use.
var recognizerResult = await _botServices.Dispatch.RecognizeAsync(stepContext.Context, cancellationToken);
// Top intent tell us which cognitive service to use.
var topIntent = recognizerResult.GetTopScoringIntent();
switch (topIntent.intent)
{
case "Mainissue":
return await stepContext.BeginDialogAsync($"{nameof(MainDialog)}.fromMain", stepContext.Values[UserInfo], cancellationToken);
case "ConfirmIntent":
return await stepContext.BeginDialogAsync(nameof(Confirm), null, cancellationToken);
case "EndBotIntent":
return await stepContext.CancelAllDialogsAsync(true, null, null, cancellationToken);
case "InverterData":
await ProcessSampleQnAAsync(stepContext, cancellationToken);
break;
default:
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"I'm sorry I don't know what you mean."), cancellationToken);
break;
}
return await stepContext.EndDialogAsync(null, cancellationToken);
}
private IList<Choice> GetChoices()
{
var cardOptions = new List<Choice>()
{
new Choice() { Value = "Product issue", Synonyms = new List<string>() { "adaptive" } },
new Choice() { Value = "Register Product", Synonyms = new List<string>() { "hero" } },
new Choice() { Value = "Online Purchase", Synonyms = new List<string>() { "hero" } },
new Choice() { Value = "Customer Grivance", Synonyms = new List<string>() { "hero" } },
new Choice() { Value = "Order Status", Synonyms = new List<string>() { "hero" } }
};
return cardOptions;
}
private Task<bool> ValidationAsync(PromptValidatorContext<string> promptContext, CancellationToken cancellationToken)
{
string value = (string)promptContext.Context.Activity.Text;
if (Regex.IsMatch(value, "^[0-9]{10}$"))
{
return Task.FromResult(true);
}
else
{
return Task.FromResult(false);
}
}
private async Task ProcessSampleQnAAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
_logger.LogInformation("ProcessSampleQnAAsync");
var results = await _botServices.SampleQnA.GetAnswersAsync(stepContext.Context);
if (results.Any())
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text(results.First().Answer), cancellationToken);
}
else
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Sorry, could not find an answer in the Q and A system."), cancellationToken);
}
}
}
You can skip the step by evaluating the condition and calling NextAsync, like this:
if (condition)
{
await stepContext.NextAsync(stepContext.Result, cancellationToken);
}
// otherwise process this step
The NextAsync immediately passes control to the next step with whatever you used as the 1st parameter. In this case, I used stepContext.Result, which is the result of the previous step that will be passed to the next step.

QnA call fails in Bot Framework, both locally and deployed

I've been trying to build a bot that uses QnA to anser the user based on categories.
I've managed to connect to QnA correctly and get the first level prompted to the user, however, the problem appears when I try to send to QnA another response from the user.
Here's the dialog that runs smoothly on it's first request.
private async Task<DialogTurnResult> InicioRRHHDialog(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
string intentForQna = IntentEnum.DISPATCH_RRHH;
var castContextForQna = (WaterfallStepContext)stepContext.Parent;
var contextForNextStep = stepContext.Context;
await GetQnaConnection(castContextForQna, intentForQna);
return await stepContext.NextAsync(contextForNextStep, cancellationToken);
}
This is the GetQnaConnection being called.
private async Task GetQnaConnection(WaterfallStepContext stepContext, string intent)
{
servicioQNA = new QnAMakerService(this._qnaService.Url, _qnaRRHHkbId, this._qnaService.QnaEndPointKey);
var mensaje = await servicioQNA.QueryQnAServiceAsync(intent, null);
await ShowResults(stepContext, mensaje);
}
And lastly the ShowResults method that I use to send the activity back to the user.
private static async Task ShowResults(WaterfallStepContext stepContext, QnAResult[] mensaje)
{
Activity outputActivity = null;
QnABotState newState = null;
var qnaAnswer = mensaje[0].Answer;
var prompts = mensaje[0].Context?.Prompts;
if (prompts == null || prompts.Length < 1)
outputActivity = MessageFactory.Text(qnaAnswer);
else
{
newState = new QnABotState
{
PreviousQnaId = mensaje[0].Id,
PreviousUserQuery = stepContext.Result.ToString()
};
outputActivity = CardHelper.GetHeroCard(qnaAnswer, prompts);
}
var outputs = new Activity[] { outputActivity };
foreach (var activity in outputs)
await stepContext.Context.SendActivityAsync(activity).ConfigureAwait(false);
}
Am I handling the Qna connection correctly?

bot frame work chatbot.Bot encounters bug

A bot doesn't go to origin!=null. I have tried taking origin as a separate step, still the same bug occurs.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Logging;
using Microsoft.Recognizers.Text.DataTypes.TimexExpression;
using Newtonsoft.Json;
namespace Microsoft.BotBuilderSamples.Dialogs
{
public class MainDialog : ComponentDialog
{
private readonly FlightBookingRecognizer _luisRecognizer;
protected readonly ILogger Logger;
// Dependency injection uses this constructor to instantiate MainDialog
public MainDialog(FlightBookingRecognizer luisRecognizer, BookingDialog bookingDialog, ILogger<MainDialog> logger)
: base(nameof(MainDialog))
{
_luisRecognizer = luisRecognizer;
Logger = logger;
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(bookingDialog);
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
IntroStepAsync,
ActStepAsync,
OriginStepAsync,
OriginConfirmAsync,
FinalStepAsync,
}));
// The initial child Dialog to run.
InitialDialogId = nameof(WaterfallDialog);
}
private const string DestinationStepMsgText = "Where would you like to travel to?";
private const string OriginStepMsgText = "Where are you traveling from?";
private async Task<DialogTurnResult> IntroStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if (!_luisRecognizer.IsConfigured)
{
await stepContext.Context.SendActivityAsync(
MessageFactory.Text("NOTE: LUIS is not configured. To enable all capabilities, add 'LuisAppId', 'LuisAPIKey' and 'LuisAPIHostName' to the appsettings.json file.", inputHint: InputHints.IgnoringInput), cancellationToken);
return await stepContext.NextAsync(null, cancellationToken);
}
// Use the text provided in FinalStepAsync or the default if it is the first time.
var messageText = stepContext.Options?.ToString() ?? "What can I help you with today?\nSay something like \"Book a flight from Paris to Berlin on March 22, 2020\"";
var promptMessage = MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput);
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = promptMessage }, cancellationToken);
}
private async Task<DialogTurnResult> ActStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if (!_luisRecognizer.IsConfigured)
{
// LUIS is not configured, we just run the BookingDialog path with an empty BookingDetailsInstance.
return await stepContext.BeginDialogAsync(nameof(BookingDialog), new BookingDetails(), cancellationToken);
}
// Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt.)
var luisResult = await _luisRecognizer.RecognizeAsync<FlightBooking>(stepContext.Context, cancellationToken);
switch (luisResult.TopIntent().intent)
{
//case FlightBooking.Intent.BookFlight:
// await ShowWarningForUnsupportedCities(stepContext.Context, luisResult, cancellationToken);
// // Initialize BookingDetails with any entities we may have found in the response.
// //var bookingDetails = new BookingDetails()
// //{
// // // Get destination and origin from the composite entities arrays.
// // Destination = luisResult.ToEntities.Airport,
// // Origin = luisResult.FromEntities.Airport,
// // TravelDate = luisResult.TravelDate,
// //};
// return await stepContext.BeginDialogAsync(nameof(BookingDialog), bookingDetails, cancellationToken);
case FlightBooking.Intent.TravellerDeatail:
//await ShowWarningForUnsupportedCities(stepContext.Context, luisResult, cancellationToken);
// Initialize BookingDetails with any entities we may have found in the response.
var bookingDetails = new BookingDetails()
{
// Get destination and origin from the composite entities arrays.
Destination = luisResult.DestinationEntities.Destination,
Origin = luisResult.OriginEntities.Origin,
TravelDate = luisResult.TravelDate,
};
if (bookingDetails.Destination == null)
{
var messageText1 = stepContext.Options?.ToString() ?? DestinationStepMsgText;
var promptMessage1 = MessageFactory.Text(messageText1, messageText1, InputHints.ExpectingInput);
await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = promptMessage1 }, cancellationToken);
var promptMessage3 = "";
return await stepContext.ReplaceDialogAsync(InitialDialogId, promptMessage3, cancellationToken);
}
if (bookingDetails.Destination != null)
{
string[] SplitDest;
string combined = "";
string collection = "";
string PreDest = bookingDetails.Destination;
string ConDest = bookingDetails.ConDestination;
try
{
if (PreDest.Length != 3)
{
var keyword = PreDest;
string[] strCity = File.ReadAllText(#"D:\Ravvise project\Attarbot2-src\AdditionalIN\Airports.txt").Split('$');
foreach (string str in strCity)
{
if (str.IndexOf(keyword, StringComparison.CurrentCultureIgnoreCase) >= 0)
{
combined += str + '$';
}
}
}
SplitDest = combined.Split("$");
foreach (var prepoll in SplitDest)
{
if (SplitDest.Length == 1)
{
ConDest = SplitDest[0];
ConDest = SplitDest[0].Split("[")[1].Replace("]", "");
}
else if (SplitDest.Length > 1)
{
string DestBody = File.ReadAllText(#"D:\Ravvise project\Attarbot2-src\AdditionalIN\DestBody.txt");
string strTemp = "";
collection = "";
foreach (string city in SplitDest)
{
if (city != "")
{
ConDest = city.Split("[")[1].Replace("]", "");
strTemp = DestBody.Replace("{ZZZ}", city);
strTemp = strTemp.Replace("{YYY}", ConDest);
collection += strTemp;
}
}
collection = collection.TrimEnd(',');
string DestTemplate = File.ReadAllText(#"D:\Ravvise project\Attarbot2-src\AdditionalIN\DestTemplate.txt");
DestTemplate = DestTemplate.Replace("{XXX}", collection);
var cardAttachment = new Attachment()
{
ContentType = "application/vnd.microsoft.card.adaptive",
Content = JsonConvert.DeserializeObject(DestTemplate.ToString()),
};
var opts = new PromptOptions
{
Prompt = new Activity
{
Attachments = new List<Attachment>() { cardAttachment },
Type = ActivityTypes.Message,
Text = "Kindly select any Combinations from below.", // You can comment this out if you don't want to display any text. Still works.
}
};
// Display a Text Prompt and wait for input
return await stepContext.PromptAsync(nameof(TextPrompt), opts);
}
}
}
catch (Exception ex) { }
return await stepContext.ContinueDialogAsync();
}
if (bookingDetails.Origin == null)
{
var messageText1 = stepContext.Options?.ToString() ?? "Wow 😍😍😍😍😍😍😍😍" + bookingDetails.Destination + " is a great place to explore.Kindly tell your Origin.";
var promptMessage1 = MessageFactory.Text(messageText1, messageText1, InputHints.ExpectingInput);
await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = promptMessage1 }, cancellationToken);
var promptMessage3 = "";
return await stepContext.ReplaceDialogAsync(InitialDialogId, promptMessage3, cancellationToken);
}
var promptMessage5 = "";
return await stepContext.ReplaceDialogAsync(InitialDialogId, promptMessage5, cancellationToken);
case FlightBooking.Intent.Greeting:
var messageText = stepContext.Options?.ToString() ?? "Gretings Mate!!!!!!😉😉😉I wink cus I wanna help you with your😃😃😃😃😃So.....Kindly tell me your travel deatails.";
var promptMessage = MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput);
await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = promptMessage }, cancellationToken);
var promptMessage2 = "";
return await stepContext.ReplaceDialogAsync(InitialDialogId, promptMessage2, cancellationToken);
// Run the BookingDialog giving it whatever details we have from the LUIS call, it will fill out the remainder.
case FlightBooking.Intent.GetWeather:
// We haven't implemented the GetWeatherDialog so we just display a TODO message.
var getWeatherMessageText = "TODO: get weather flow here";
var getWeatherMessage = MessageFactory.Text(getWeatherMessageText, getWeatherMessageText, InputHints.IgnoringInput);
await stepContext.Context.SendActivityAsync(getWeatherMessage, cancellationToken);
break;
default:
// Catch all for unhandled intents
var didntUnderstandMessageText = $"Sorry, I didn't get that. Please try asking in a different way (intent was {luisResult.TopIntent().intent})";
var didntUnderstandMessage = MessageFactory.Text(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IgnoringInput);
await stepContext.Context.SendActivityAsync(didntUnderstandMessage, cancellationToken);
break;
}
return await stepContext.NextAsync(null, cancellationToken);
}
// Shows a warning if the requested From or To cities are recognized as entities but they are not in the Airport entity list.
// In some cases LUIS will recognize the From and To composite entities as a valid cities but the From and To Airport values
// will be empty if those entity values can't be mapped to a canonical item in the Airport.
private async Task<DialogTurnResult> OriginStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var bookingDetails = (BookingDetails)stepContext.Options;
bookingDetails.ConDestination = (string)stepContext.Result;
if (bookingDetails.Origin == null)
{
var messageText1 = stepContext.Options?.ToString() ?? "Wow 😍😍😍😍😍😍😍😍" + bookingDetails.Destination + " is a great place to explore.Kindly tell your Origin.";
var promptMessage1 = MessageFactory.Text(messageText1, messageText1, InputHints.ExpectingInput);
await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = promptMessage1 }, cancellationToken);
}
var promptMessage3 = "";
return await stepContext.ReplaceDialogAsync(InitialDialogId, promptMessage3, cancellationToken);
}
private async Task<DialogTurnResult> OriginConfirmAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
string[] SplitOrigin;
string combined = "";
string collection = "";
string PConOrigin = "";
var bookingDetails = (BookingDetails)stepContext.Options;
bookingDetails.Origin = (string)stepContext.Result;
string PreOrigin = bookingDetails.Origin;
string ConOrigin = bookingDetails.ConOrigin;
try
{
if (PreOrigin.Length != 3)
{
var keyword = PreOrigin;
string[] strCity = File.ReadAllText("E:/Airports.txt").Split('$');
foreach (string str in strCity)
{
if (str.IndexOf(keyword, StringComparison.CurrentCultureIgnoreCase) >= 0)
{
combined += str + '$';
}
}
}
SplitOrigin = combined.Split("$");
foreach (var prepoll in SplitOrigin)
{
if (SplitOrigin.Length == 1)
{
ConOrigin = SplitOrigin[0];
ConOrigin = SplitOrigin[0].Split("[")[1].Replace("]", "");
}
else if (SplitOrigin.Length > 1)
{
string DestBody = File.ReadAllText("E:/DestBody.txt");
string strTemp = "";
collection = "";
foreach (string city in SplitOrigin)
{
if (city != "")
{
ConOrigin = city.Split("[")[1].Replace("]", "");
strTemp = DestBody.Replace("{ZZZ}", city);
strTemp = strTemp.Replace("{YYY}", ConOrigin);
collection += strTemp;
}
}
collection = collection.TrimEnd(',');
string DestTemplate = File.ReadAllText("E:/DestTemplate.txt");
DestTemplate = DestTemplate.Replace("{XXX}", collection);
var cardAttachment = new Attachment()
{
ContentType = "application/vnd.microsoft.card.adaptive",
Content = JsonConvert.DeserializeObject(DestTemplate.ToString()),
};
var opts = new PromptOptions
{
Prompt = new Activity
{
Attachments = new List<Attachment>() { cardAttachment },
Type = ActivityTypes.Message,
Text = "Kindly select any Combinations from below.", // You can comment this out if you don't want to display any text. Still works.
}
};
// Display a Text Prompt and wait for input
return await stepContext.PromptAsync(nameof(TextPrompt), opts);
}
}
}
catch (Exception ex) { }
return await stepContext.NextAsync(bookingDetails.ConOrigin, cancellationToken);
}
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
// If the child dialog ("BookingDialog") was cancelled, the user failed to confirm or if the intent wasn't BookFlight
// the Result here will be null.
if (stepContext.Result is BookingDetails result)
{
// Now we have all the booking details call the booking service.
// If the call to the booking service was successful tell the user.
var timeProperty = new TimexProperty(result.TravelDate);
var travelDateMsg = timeProperty.ToNaturalLanguage(DateTime.Now);
var messageText = $"I have you booked to {result.Destination} from {result.Origin} on {travelDateMsg}";
var message = MessageFactory.Text(messageText, messageText, InputHints.IgnoringInput);
await stepContext.Context.SendActivityAsync(message, cancellationToken);
}
// Restart the main dialog with a different message the second time around
var promptMessage = "What else can I do for you?";
return await stepContext.ReplaceDialogAsync(InitialDialogId, promptMessage, cancellationToken);
}
}
}
After destination != null, bot doesn't go to bookingdetails.origin= null. If it is not possible please guide any other way to do it like different steps to get different values. I tried to do it but still, the same problem occurs.

Bot (V4) shows inconsistent results in diff channel for basic choiceprompt

I am getting inconsistent result for a basic choice prompt in Bot using V4 in different channel. Also when i enter invalid options, it sometime appends the option list in some channle. not sure, what i am doing wrong.
e.g in Emulator
it shows like this which is perfect most of the time
e.g when i publish to azure web app bot, in test in web chat. it does not show choice in boxes but show as suggestion list and also appends the list of options after i enter incorrect option.
see in portal Web chat
e.g in Microsoft Teams, it does not show choice in boxes but shows as suggestion list and also appends the list of options after i enter incorrect option.
see here in Microsoft Teams
My code is like below
public MytestBotRootDialog()
: base(nameof(PowerBICSSRootDialog))
{
this.rootChoices = new List<Choice>();
this.rootChoices.Add(new Choice { Value = "Service"});
this.rootChoices.Add(new Choice { Value = "Desktop"});
this.rootChoices.Add(new Choice { Value = "Other"});
var waterfallSteps = new WaterfallStep[]
{
PromptForRootChoicesAsync,
RouteToSpecificDialogAsync,
RepeatRootChoiceAsync
};
AddDialog(new WaterfallDialog(rootdialog, waterfallSteps));
AddDialog(new ChoicePrompt(rootchoicesprompt));
AddDialog(new ContentPackRootDialog());
}
private async Task<DialogTurnResult> PromptForRootChoicesAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var opts = new PromptOptions
{
Prompt = MessageFactory.Text("Select the Area"),
Choices = rootChoices,
RetryPrompt = MessageFactory.Text("Please enter valid Area"),
};
return await stepContext.PromptAsync(rootchoicesprompt, opts, cancellationToken);
}
private async Task<DialogTurnResult> RouteToSpecificDialogAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var selectedchoice = stepContext.Result as FoundChoice;
var result = selectedchoice.Value.ToLowerInvariant();
if (selectedchoice.Index == 0 || result.Contains("service"))
{
var context = stepContext.Context;
await context.SendActivityAsync($"You Selected Service");
return await stepContext.NextAsync();
}
else if (selectedchoice.Index == 1 || result.Contains("desktop"))
{
var context = stepContext.Context;
await context.SendActivityAsync($"You Selected Dekstop");
return await stepContext.NextAsync();
}
else if (selectedchoice.Index == 2 || result.Contains("other"))
{
return await stepContext.BeginDialogAsync(nameof(ContentPackRootDialog));
}
else
return await stepContext.NextAsync();
}
private async Task<DialogTurnResult> RepeatRootChoiceAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
return await stepContext.ReplaceDialogAsync(rootdialog, null, cancellationToken);
}

Categories