Reputation: 1246
We're experimenting with the MS Bot Framework and haven't quite worked out how to do this scenario:
We have a LUIS Dialog (type <object>
), which is working correctly and is trained properly. To use the common sandwich example, the basics of what LUIS intent is looking for is the user asking for the status of an order. If the order number was provided in the question ("What is the status of order 1234?"), then the LUIS dialog does the lookup and reports the status directly (which is all currently working).
However, if the user just triggers the intent without providing the order number ("I'd like to look up the status of an order."), I'd like to launch another dialog/form to ask the user if they'd like to look up the order by address or order number, and then do the appropriate DB lookup based on how they answer.
I'm just not sure how to configure the Form/Dialog (or even which is best in this case) to do a different lookup based on if they choose address or number lookup.
Here's the intent so far:
private readonly BuildFormDelegate<OrderStatusDialog> OrderStatusDelegate;
[LuisIntent(nameof(LuisIntents.OrderStatus))]
public async Task OrderStatus(IDialogContext context, LuisResult result)
{
// Order number(s) were provided
if (result.Entities.Any(Entity => Entity.Type == nameof(LuisEntityTypes.OrderNumber)))
{
// Loop in case they asked about multiple orders
foreach (var entity in result.Entities.Where(Entity => Entity.Type == nameof(LuisEntityTypes.OrderNumber)))
{
var orderNum = entity.Entity;
// Call webservice to check status
var request = new RestRequest(Properties.Settings.Default.GetOrderByNum, Method.GET);
request.AddUrlSegment("num", orderNum);
var response = await RestHelper.SendRestRequestAsync(request);
var parsedResponse = JObject.Parse(response);
if ((bool)parsedResponse["errored"])
{
await context.PostAsync((string)parsedResponse["errMsg"]);
continue;
}
// Grab status from returned JSON
var status = parsedResponse["orderStatus"].ToString();
await context.PostAsync($"The status of order {orderNum} is {status}");
}
context.Wait(MessageReceived);
}
// Order number was not provided
else
{
var orderStatusForm = new FormDialog<OrderStatusDialog>(new OrderStatusDialog(), OrderStatusDelegate,
FormOptions.PromptInStart);
context.Call<OrderStatusDialog>(orderStatusForm, CallBack);
}
}
private async Task CallBack(IDialogContext context, IAwaitable<object> result)
{
context.Wait(MessageReceived);
}
And the form:
public enum OrderStatusLookupOptions
{
Address,
OrderNumber
}
[Serializable]
public class OrderStatusDialog
{
public OrderStatusLookupOptions? LookupOption;
public static IForm<OrderStatusDialog> BuildForm()
{
return new FormBuilder<OrderStatusDialog>()
.Message("In order to look up the status of a order, we will first need either the order number or your delivery address.")
.Build();
}
}
Upvotes: 1
Views: 760
Reputation: 14787
The FormFlow route is a valid option. What is missing in your form flow is asking for the address/order number after the lookup option is selected.
What you can do in that case is adding two more fields to the OrderStatusDialog
class: OrderNumber
and DeliveryAddress
.
Then you need to use the selected OrderStatusLookupOptions
to activate/deactivate the next field.
The code, from the top of my head, would be something like:
[Serializable]
public class OrderStatusDialog
{
public OrderStatusLookupOptions? LookupOption;
public int OrderNumber;
public string DeliveryAddress
public static IForm<OrderStatusDialog> BuildForm()
{
return new FormBuilder<OrderStatusDialog>()
.Message("In order to look up the status of a order, we will first need either the order number or your delivery address.")
.Field(nameof(OrderStatusDialog.LookupOption))
.Field(new FieldReflector<OrderStatusDialog>(nameof(OrderStatusDialog.OrderNumber))
.SetActive(state => state.LookupOption == OrderStatusLookupOptions.OrderNumber))
.Field(new FieldReflector<OrderStatusDialog>(nameof(OrderStatusDialog.DeliveryAddress))
.SetActive(state => state.LookupOption == OrderStatusLookupOptions.Address))
.Build();
}
}
Then on your Callback method you will receive the form filled and you can do the DB lookup.
Alternatively, you can just use PromptDialogs and guide the user through the same experience. Take a look to the MultiDialogs sample to see the different alternatives.
I added a working sample on this here.
Upvotes: 2