in libraries/Microsoft.Bot.Builder.Dialogs.Adaptive/AdaptiveDialog.cs [1562:1684]
private List<EntityInfo> AssignEntities(ActionContext actionContext, Dictionary<string, List<EntityInfo>> entities, EntityAssignments existing, string lastEvent)
{
var assignments = new EntityAssignments();
if (!actionContext.State.TryGetValue<string[]>(DialogPath.ExpectedProperties, out var expected))
{
expected = Array.Empty<string>();
}
// default op from the last Ask action.
var askDefaultOp = actionContext.State.GetValue<JObject>(DialogPath.DefaultOperation);
// default operation from the current adaptive dialog.
var defaultOp = dialogSchema.Schema[DefaultOperationKey]?.ToObject<JObject>();
var nextAssignment = existing.NextAssignment();
var candidates = (from candidate in RemoveOverlappingPerProperty(Candidates(entities, expected, lastEvent, nextAssignment, askDefaultOp, defaultOp))
orderby
candidate.IsExpected descending,
candidate.Operation == DefaultOperation(candidate, askDefaultOp, defaultOp) descending
select candidate).ToList();
var usedEntities = new HashSet<EntityInfo>(from candidate in candidates select candidate.Value);
List<string> expectedChoices = null;
var choices = new List<EntityAssignment>();
while (candidates.Any())
{
var candidate = candidates.First();
// Alternatives are either for the same entity or from different roots
var alternatives = (from alt in candidates
where candidate.Value.Overlaps(alt.Value) && (!candidate.Value.SharesRoot(alt.Value) || candidate.Value == alt.Value)
select alt).ToList();
candidates = candidates.Except(alternatives).ToList();
foreach (var alternative in alternatives)
{
usedEntities.Add(alternative.Value);
}
if (candidate.IsExpected && candidate.Value.Name != UtteranceKey)
{
// If expected binds entity, drop unexpected alternatives unless they have an explicit operation
alternatives.RemoveAll(a => !a.IsExpected && a.Value.Operation == null);
}
// Find alternative that covers the largest amount of utterance
candidate = (from alternative in alternatives orderby alternative.Value.Name == UtteranceKey ? 0 : alternative.Value.End - alternative.Value.Start descending select alternative).First();
// Remove all alternatives that are fully contained in largest
alternatives.RemoveAll(a => candidate.Value.Covers(a.Value));
var mapped = false;
if (candidate.Operation == AdaptiveEvents.ChooseEntity)
{
// Property has resolution so remove entity ambiguity
var entityChoices = existing.Dequeue(actionContext);
candidate.Operation = entityChoices.Operation;
if (candidate.Value.Value is JArray values && values.Count > 1)
{
// Resolve ambiguous response to one of the original choices
var originalChoices = entityChoices.Value.Value as JArray;
var intersection = values.Intersect(originalChoices);
if (intersection.Any())
{
candidate.Value.Value = intersection;
}
}
}
else if (candidate.Operation == AdaptiveEvents.ChooseProperty)
{
choices = nextAssignment.Alternatives.ToList();
var choice = choices.Find(a => MatchesAssignment(candidate.Value, a));
if (choice != null)
{
// Resolve choice, pretend it was expected and add to assignments
expectedChoices = new List<string>();
choice.IsExpected = true;
choice.Alternative = null;
if (choice.Property != null)
{
expectedChoices.Add(choice.Property);
}
else if (choice.ExpectedProperties != null)
{
expectedChoices.AddRange(choice.ExpectedProperties);
}
AddAssignment(choice, assignments);
choices.RemoveAll(c => c.Value.Overlaps(choice.Value));
mapped = true;
}
}
candidate.AddAlternatives(alternatives);
if (!mapped)
{
AddAssignment(candidate, assignments);
}
}
if (expectedChoices != null)
{
// When choosing between property assignments, make the assignments be expected.
if (expectedChoices.Any())
{
actionContext.State.SetValue(DialogPath.ExpectedProperties, expectedChoices);
}
// Add back in any non-overlapping choices that have not been resolved
while (choices.Any())
{
var choice = choices.First();
var overlaps = from alt in choices where choice.Value.Overlaps(alt.Value) select alt;
choice.AddAlternatives(overlaps);
AddAssignment(choice, assignments);
choices.RemoveAll(c => c.Value.Overlaps(choice.Value));
}
existing.Dequeue(actionContext);
}
var operations = new EntityAssignmentComparer(dialogSchema.Schema[OperationsKey]?.ToObject<string[]>() ?? Array.Empty<string>());
MergeAssignments(assignments, existing, operations);
return usedEntities.ToList();
}