Confirmation Prompt with ParseMessage sending text over to Luis, and then I return true or false. I've set debugger in this file and verified response returns bool.
Code in question. When I get result back it says Cannot implicitly convert IAwaitable<bool> to bool, which is where Convert.ToBoolean() came in; still no luck however.
How can I check the returned bool so that I can verify results in this if statement.
In this current code sample it just sends back a message in bot emulator saying:
Exception: Unable to cast object of type ‘Microsoft.Bot.Builder.Internals.Fibers.Wait`2[Microsoft.Bot.Builder.Dialogs.Internals.DialogTask,System.Boolean]’ to type ‘System.IConvertible’.
Edit: Updated for more code
RootDialog.cs
private async Task SendWelcomeMessageAsync(IDialogContext context)
{
await context.PostAsync("Hi, I'm the Basic Multi Dialog bot. Let's get started.");
context.Call(new ConfirmLuisPrompt(), this.ConfirmLuisPromptAfter);
}
private async Task ConfirmLuisPromptAfter(IDialogContext context, IAwaitable<bool> result)
{
//var res = Convert.ToBoolean(result);
var confirm = await result;
if (confirm)
{
//yes
context.Call(FormDialog.FromForm(PersonInfo.BuildForm, FormOptions.PromptInStart), this.PersonInfoAfter);
}else
{
//no
await context.PostAsync($"Ok, let's get you started");
context.Call(FormDialog.FromForm(PatientInfo.BuildForm, FormOptions.PromptInStart), InHospital);
}
}
ConfirmLuisPrompt.cs
[Serializable]
public class ConfirmLuisPrompt : IDialog<bool>
{
public async Task StartAsync(IDialogContext context)
{
await context.PostAsync("Do you have insurance?");
context.Wait(this.MessageReceivedAsync);
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
dynamic m = await result;
var message = m.Text.ToString();
//context.Wait( ParseMessage(context, message));
bool response = await ParseMessage(message);
context.Done(response);
//context.PostAsync(response.toString());
}
public bool ParseMessage(string input)
{
LuisClient luisClient = new LuisClient("<key1>", "<key2>");
Task<LuisResult> resultTask = luisClient.Predict(input);
resultTask.Wait();
LuisResult result = resultTask.Result;
if (result.TopScoringIntent.Name == "Yes")
{
return true;
}
else if (result.TopScoringIntent.Name == "No")
{
return false;
}
else
{
return false;
}
}
}
You just need to do the following in your ConfirmLuisPromptAfter method:
var confirm = await result;
if (confirm)
{
...
}
Do not convert just do: result.GetAwaiter().GetResult() which should return a boolean
Related
I'm creating a bot using Bot Framework, in web channel and direct line.
i Have a luis dialog which call some forms ( flow form ) .
I have to ask for each form the contract number and some other data . How to store just one time contract number and to go ahead to other field and that to response the right answer for that contract number of the user.
i was trying to ask in the begging the contract number and name of the person and to check if they match..if yes to let the user to use the bot...but i cannot find the right logic code.
Can you help me please with any idea how to ask just one time the contract number and to use in all my dialog and forms ?
Below is my code :
MessagesController.cs :
public class MessagesController : ApiController
{
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
var connector = new ConnectorClient(new Uri(activity.ServiceUrl));
Activity isTypingReply = activity.CreateReply();
isTypingReply.Type = ActivityTypes.Typing;
await connector.Conversations.ReplyToActivityAsync(isTypingReply);
if (activity.Type == ActivityTypes.Message)
{
activity.Locale = "en-US";
Helpers.SaveActivityDataToDB(activity);
await Conversation.SendAsync(activity, () => new LuisDialog());
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
Luis.cs
[LuisIntent("Greeting")]
public async Task Greeting(IDialogContext context, LuisResult result)
{
await context.SayAsync(text: "Welcome");
}
[LuisIntent("balance")]
public async Task balance(IDialogContext context, LuisResult result)
{
var balanca = new FormDialog<BalanceForm>(
new BalanceForm(),
BalanceForm.BuildForm,
FormOptions.PromptInStart,
result.Entities);
context.Call<BalanceForm>(balanca, BalanceCompleted);
}
private async Task BalanceCompleted(IDialogContext context, IAwaitable<BalanceForm> result)
{
BalanceForm form = null;
try
{
form = await result;
}
catch (OperationCanceledException)
{
}
if (form == null)
{
await context.PostAsync("Try again please!");
}
else
{
//call the LossForm service to complete the form fill
var message = $"Thnx";
await context.PostAsync(message);
}
context.Wait(this.MessageReceived);
}
BalanceForm.cs
[Serializable]
public class BalanceForm
{
[Prompt("What is your contract number?")]
public string contract;
public static IForm<BalanceForm> BuildForm()
{
OnCompletionAsyncDelegate<BalanceForm> wrapUpRequest = async
(context, state) =>
{
using (BotModelDataContext BotDb = new BotModelDataContext())
{
//search in database
string wrapUpMessage = "Dear " + house.Firstname + "," + "your balance is " + house.Balance;
await context.PostAsync(wrapUpMessage);
};
return new FormBuilder<BalanceForm>()
.Message("We have to ask you some information")
.Field(nameof(contract))
.OnCompletion(wrapUpRequest)
//.Confirm("Are you sure: Yes or No ")
.Build();
}
}
}
Can you help me please with any idea how to ask just one time the contract number and to use in all my dialog and forms ?
Based on your requirement, I create a sample to prompt user for contract number and save the value in UserData so that I can get the contract number that user provided from UserData in child dialog(s). The following sample code is for your reference.
In RootDialog:
[Serializable]
public class RootDialog : IDialog<object>
{
public Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
return Task.CompletedTask;
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var activity = await result as Activity;
// calculate something for us to return
int length = (activity.Text ?? string.Empty).Length;
var contractnumber = "";
if (!context.UserData.TryGetValue<string>("contract_number", out contractnumber))
{
//prompt for contract number
PromptDialog.Text(
context: context,
resume: AfterGetContractNumber,
prompt: "Please provide your contract number.",
retry: "Please try again."
);
}
else
{
await context.Forward(new BaiscLuisDialog(), AfterLuisDialog, context.Activity, CancellationToken.None);
}
}
private async Task AfterGetContractNumber(IDialogContext context, IAwaitable<string> result)
{
string contractnumber = await result;
context.UserData.SetValue<string>("contract_number", contractnumber);
await context.PostAsync($"OK, received your contract number: {contractnumber}.");
context.Done(this);
}
private async Task AfterLuisDialog(IDialogContext context, IAwaitable<object> result)
{
context.Wait(MessageReceivedAsync);
}
}
In BaiscLuisDialog:
[LuisIntent("balance")]
public async Task BalanceIntent(IDialogContext context, LuisResult result)
{
//call BalanceForm
//and pass contract number that user provided to BalanceForm contract_number
var balanca = new Microsoft.Bot.Builder.FormFlow.FormDialog<BalanceForm>(new BalanceForm() { contract_number = context.UserData.GetValue<string>("contract_number") },
BalanceForm.BuildForm,
Microsoft.Bot.Builder.FormFlow.FormOptions.PromptInStart,
result.Entities);
context.Call<BalanceForm>(balanca, BalanceCompleted);
}
private async Task BalanceCompleted(IDialogContext context, IAwaitable<BalanceForm> result)
{
context.Wait(MessageReceived);
}
In BalanceForm:
[Serializable]
public class BalanceForm
{
public string contract_number;
[Prompt("What is your contract number?")]
public string contract;
public string your_other_field;
public static IForm<BalanceForm> BuildForm()
{
OnCompletionAsyncDelegate<BalanceForm> wrapUpRequest = async
(context, state) =>
{
//using (BotModelDataContext BotDb = new BotModelDataContext())
//{
// //search in database
// string wrapUpMessage = "Dear " + house.Firstname + "," + "your balance is " + house.Balance;
// await context.PostAsync(wrapUpMessage);
//};
//your code logic here
var contractnumber = "";
context.UserData.TryGetValue<string>("contract_number", out contractnumber);
string wrapUpMessage = " Form completed! Your contract number is " + contractnumber;
var replymes = context.MakeMessage();
replymes.Text = wrapUpMessage;
await context.PostAsync(replymes);
};
return new FormBuilder<BalanceForm>()
.Message("We have to ask you some information")
.Field(new FieldReflector<BalanceForm>(nameof(contract)).SetActive(state => state.contract_number == null))
.Field(nameof(your_other_field))
.OnCompletion(wrapUpRequest)
//.Confirm("Are you sure: Yes or No ")
.Build();
}
}
Test result:
I'm working with Microsoft's BotFramework in C#. I have a LUIS Intent, and from there I want to fire a FormFlow, but I honestly don't know how to do it.
[LuisModel("35554cdf-92ae-4413-*", "*")]
[Serializable]
public class LuisIntegration : LuisDialog<Object>
{
public BuildFormDelegate<Cotizacion> _configurecotizacion;
public BuildFormDelegate<Cotizacion> LuisDialog(BuildFormDelegate<Cotizacion> configureCotizacion) => _configurecotizacion = configureCotizacion;
[LuisIntent("Cotizar")]
public async Task CotizarAsync(IDialogContext context, LuisResult result)
{
var entitiesArray = result.Entities;
var form = new FormDialog<Cotizacion>(new Cotizacion(), Cotizacion.BuildForm, FormOptions.PromptInStart, result.Entities);
context.Call(new FormDialog<Cotizacion>(new Cotizacion(), this._configurecotizacion, FormOptions.PromptInStart), ResumeAfterCallback);
}
private async Task ResumeAfterCallback(IDialogContext context, IAwaitable<Cotizacion> result)
{
Cotizacion coti = null;
try
{
coti = await result;
}
catch (OperationCanceledException)
{
await context.PostAsync("Cancelaste la cotización.");
return;
}
//context.Wait(MessageReceived);
}
}
public static IForm<Cotizacion> BuildForm()
{
return new FormBuilder<Cotizacion>()
.Message("¡Listo! Necesito algunos datos para cotizar, empecemos.")
.Field(nameof(Nombre))
.Field(nameof(Apellido))
.Field(nameof(Provincia))
.Field(nameof(Localidad))
.Field(nameof(Edad))
.Field(new FieldReflector<Cotizacion>(nameof(Marca))
.SetType(null)
.SetDefine(async (state, field) =>
{
foreach (var prod in await Gets.ObtenerMarcasAsync())
field
.AddDescription(prod, prod)
.AddTerms(prod, prod);
return await Task.FromResult(true);
}))
.Field(new FieldReflector<Cotizacion>(nameof(Modelo))
.SetType(null)
.SetDefine(async (state, field) =>
{
foreach (var prod in await Gets.ObtenerModelosAsync(state.Marca))
field
.AddDescription(prod, prod)
.AddTerms(prod, prod);
return await Task.FromResult(true);
}))
.Field(nameof(Anio))
.Field(nameof(Version))
.Field(nameof(Email))
.Field(nameof(Telefono))
.Field(nameof(Sexo))
.Confirm("Estamos a punto de cotizar tu auto, ¿está correcta la información que ingresaste?")
.OnCompletion(async (context, state) => { ... }
The FormFlow alone works perfectly, the problem is when I added the LuisIntent.
I fixed the problem calling the FormFlow, the problem now is that is not prompting the confirm, therefore is not finishing. It hangs in the last property of the form. I updated the code.
Edit 2:
private async Task ResumeAfterCallback(IDialogContext context, IAwaitable<Cotizacion> result)
{
Cotizacion coti = null;
try
{
coti = await result;
}
catch (OperationCanceledException)
{
await context.PostAsync("Cancelaste la cotización.");
return;
}
context.Wait(MessageReceived);
}
Edit 3:
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
await Conversation.SendAsync(activity, () => new LuisIntegration());
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
Update: the (final) issue is in the definition of the DefineAsyncDelegate parameter to FieldReflector's SetDefine method:
public delegate Task<bool> DefineAsyncDelegate<T>(T state, Field<T> field) where T : class;
It is not explicitly required to be an async method, but since the implementation above uses async calls within it ('Gets.ObtenerXXXAsync()'), the method should return a bool and not a wrapped Task.FromResult<bool>.
If the async was omitted, and the Gets.ObtenerXXXAsync() was called using a Task.Run(...).Result, for example, then it would be correct to return a Task.FromResult(true).
....
Maybe your bot is obsessed with 'Sexo'... :)
Why do you need your BuildFormDelegate<Cotizacion> member variables? Your CotizarAsync method could just look like this:
[LuisIntent("Cotizar")]
public async Task CotizarAsync(IDialogContext context, IAwaitable<IMessageActivity> activity, LuisResult result)
{
context.Call(new FormDialog<Cotizacion>(new Cotizacion(), Cotizacion.BuildForm, FormOptions.PromptInStart, result.Entities),
ResumeAfterCallback);
}
which just passes Cotizacion.BuildForm to the FormDialog constructor.
I tried a cut-down version in my LUIS bot (with just Nombre and Apellido in Cotizacion) and it worked OK - the ResumeAfterCallback was called successfully.
Below are the source Code for communicating to LUIS- AI from my Bot application. When I try to communicate I am always getting Access Denied response. I don't know what I am missing here.
[LuisModel("8c9285fb-198a-4f49-8fe4-b08ac5541ac2", "5c47c63887e346c2aee24d1755e07d29")]
[Serializable]
public class LUISDialog:LuisDialog<RoomReservation>
{
private readonly BuildFormDelegate<RoomReservation> Reservation;
public LUISDialog(BuildFormDelegate<RoomReservation> reservceRoom)
{
this.Reservation = reservceRoom;
}
[LuisIntent("")]
[LuisIntent("None")]
public async Task None(IDialogContext dialogContext, LuisResult luisResult)
{
await dialogContext.PostAsync("I am sorry I don't know what you mean ");
dialogContext.Wait(MessageReceived);
}
[LuisIntent("Greeting")]
public async Task Greeting(IDialogContext dialogContext, LuisResult luisResult)
{
dialogContext.Call(new GreetingDialog(), CallBack);
}
private async Task CallBack(IDialogContext context, IAwaitable<object> result)
{
context.Wait(MessageReceived);
}
[LuisIntent("Reservation")]
public async Task RoomReservation(IDialogContext dialogContext, LuisResult luisResult)
{
FormDialog<RoomReservation> enrollmentForm =new FormDialog<RoomReservation>(new RoomReservation(),this.Reservation, FormOptions.PromptInStart);
dialogContext.Call(enrollmentForm, CallBack);
}
[LuisIntent("QueryAmenities")]
public async Task QueryAmenities(IDialogContext dialogContext, LuisResult luisResult)
{
foreach (var entity in luisResult.Entities.Where(entity=>entity.Type=="Amenity"))
{
var value = entity.Entity.ToLower();
if (value == "pool" || value == "gym" || value == "wifi" || value == "towels")
{
await dialogContext.PostAsync("Yes we have that");
dialogContext.Wait(MessageReceived);
return;
}
await dialogContext.PostAsync("I'am sorry we don't have that");
dialogContext.Wait(MessageReceived);
return;
}
await dialogContext.PostAsync("I'am sorry we don't have that");
dialogContext.Wait(MessageReceived);
}
}
Screen shot of error I am getting
Controller Code
[BotAuthentication]
public class MessagesController : ApiController
{
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
//ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
//// calculate something for us to return
//int length = (activity.Text ?? string.Empty).Length;
//// return our reply to the user
//Activity reply = activity.CreateReply($"You sent {activity.Text} which was {length} characters");
//await connector.Conversations.ReplyToActivityAsync(reply);
// await Conversation.SendAsync(activity, () => HotelBotDialog.dialog);
await Conversation.SendAsync(activity, MakeLuisDialog);
}
else
{
await HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
LUIS Intent
Please help me to resolve this
I just tested a connection to your LUIS and it seems to be working: https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/8c9285fb-198a-4f49-8fe4-b08ac5541ac2?subscription-key=5c47c63887e346c2aee24d1755e07d29&verbose=true&q=hello
Probrably you are using the endpoint key instead the Programmatic Key API. Check this:
enter image description here
I have a LuisDialog which calls a particular dialog if an intent is found. There,on the basis of entity extracted, I am calling another dialog, which seems to be throwing some unhandled exception.
Here is the intent in my root LuiDialog calling BuyDialog(I'm saving the LuisResult for later use)-
[LuisIntent("Buy")]
public async Task Buy(IDialogContext context, LuisResult result)
{
var cts = new CancellationTokenSource();
await context.PostAsync("you want to buy");
context.PrivateConversationData.SetValue<LuisResult>("luis", result);
context.Call(new BuyDialog(), ResumeAfterBuyDialog);
}
private async Task ResumeAfterBuyDialog(IDialogContext context, IAwaitable<bool> result)
{
var success = await result;
if (success)
{
await context.PostAsync("How else can I help you?");
context.Wait(MessageReceived);
}
}
Here is my BuyDialog calling another dialog(BookDialog) if entity contains "book" string-
else if(is_book)
{
await context.PostAsync("You selected book category");
context.Call(new BookDialog(),BookFormComplete);
context.Done(true);
}
private async Task BookFormComplete(IDialogContext context, IAwaitable<bool> result)
{
var BookResult = await result;
//do something if result is true
}
And here is my BookDialog-
[Serializable]
public class BookDialog : IDialog<object>
{
public async Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
}
public async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
var message = await argument;
if (message.Text == "mtl100")
{
await context.PostAsync("Correct");
context.Done(true);
}
else
{
await context.PostAsync("Please enter valid course code");
context.Wait(MessageReceivedAsync);
}
I know the problem is in that second context.call(the call to bookDialog), because if I remove it the code works perfectly fine. Right now, I'm getting "sorry, my bot code is having an issue" when it reaches there. Any idea what's wrong?
The context.Done(true) that you are doing in the second dialog must be in the BookDialog ResumeAfter<T> method (BookFormComplete)
The 'Is Book' path should looks like:
else if(is_book)
{
await context.PostAsync("You selected book category");
context.Call(new BookDialog(),BookFormComplete);
}
I've updated my NuGet packages to use the version 1.2.0.1 of the Microsoft Bot Framework.
Some breaking changes were reported here, and I managed to fix the build errors. But the application is not working anymore..
I have two problems:
The code throws an InvalidIntentHandlerException when I send a message an utterance to the controller.
In my 'intent' method (decorated with the LuisIntent attribute) it was possible to read the value of the entities. Like so:
[Serializable]
[LuisModel("xxxxx", "xxxx")]
public class BookFlightDialog : LuisDialog<BookFlightForm>
{
private readonly BuildFormDelegate<BookFlightForm> BuildForm;
internal BookFlightDialog(BuildFormDelegate<BookFlightForm> buildForm)
{
BuildForm = buildForm;
}
[LuisIntent("")]
[LuisIntent("None")]
public async Task None(IDialogContext context, LuisResult result)
{
await context.PostAsync("I'm sorry. I didn't understand you.");
context.Wait(MessageReceived);
}
[LuisIntent("BookAFlight")]
public async Task BookAFlight(IDialogContext context, LuisResult result)
{
var form = new BookFlightForm();
// var entities = new List<EntityRecommendation>(result.Entities);
var locations = result.Entities.Where(e => e.Type.Equals("builtin.geography") || e.Type.Equals("builtin.geography.city")).OrderBy(e => e.StartIndex);
if (locations.Any())
{
form.LocationFrom = locations.First().Name;
if (locations.Count() == 2)
{
form.LocationTo = locations.Skip(1).First().Name;
}
}
var date = result.Entities.FirstOrDefault(e => e.Type == "builtin.datetime.date");
if (date != null) form.DepartureDate = DateTime.Parse(date.Name);
var formDialog = new FormDialog<BookFlightForm>(form, BuildForm, FormOptions.PromptInStart);
context.Call(formDialog, OnComplete);
}
private async Task OnComplete(IDialogContext context, IAwaitable<BookFlightForm> result)
{
BookFlightForm booking;
try
{
booking = await result;
}
catch (OperationCanceledException)
{
await context.PostAsync("Ok, see you later.");
return;
}
if (booking != null)
{
var service = new SkyScannerService();
var possibilities = await service.Search(booking);
await context.PostAsync(possibilities);
}
else
{
await context.PostAsync("Form returned empty response!");
}
context.Wait(MessageReceived);
}
}
How do I fix the exception and how do I read the value of the entities?
Thanks once again!
This is because you are not using inbuilt LuisResult class by having using LuisResult = Bots.Results.LuisResult;. Replace it with using Microsoft.Bot.Builder.Luis.Models;.