Spaces:
Build error
Build error
Validify-testbot-1
/
botbuilder-python
/libraries
/botbuilder-dialogs
/botbuilder
/dialogs
/choices
/choice_recognizers.py
# Copyright (c) Microsoft Corporation. All rights reserved. | |
# Licensed under the MIT License. | |
from typing import List, Union | |
from recognizers_number import NumberModel, NumberRecognizer, OrdinalModel | |
from recognizers_text import Culture | |
from .choice import Choice | |
from .find import Find | |
from .find_choices_options import FindChoicesOptions | |
from .found_choice import FoundChoice | |
from .model_result import ModelResult | |
class ChoiceRecognizers: | |
"""Contains methods for matching user input against a list of choices.""" | |
def recognize_choices( | |
utterance: str, | |
choices: List[Union[str, Choice]], | |
options: FindChoicesOptions = None, | |
) -> List[ModelResult]: | |
""" | |
Matches user input against a list of choices. | |
This is layered above the `Find.find_choices()` function, and adds logic to let the user specify | |
their choice by index (they can say "one" to pick `choice[0]`) or ordinal position | |
(they can say "the second one" to pick `choice[1]`.) | |
The user's utterance is recognized in the following order: | |
- By name using `find_choices()` | |
- By 1's based ordinal position. | |
- By 1's based index position. | |
Parameters: | |
----------- | |
utterance: The input. | |
choices: The list of choices. | |
options: (Optional) Options to control the recognition strategy. | |
Returns: | |
-------- | |
A list of found choices, sorted by most relevant first. | |
""" | |
if utterance is None: | |
utterance = "" | |
# Normalize list of choices | |
choices_list = [ | |
Choice(value=choice) if isinstance(choice, str) else choice | |
for choice in choices | |
] | |
# Try finding choices by text search first | |
# - We only want to use a single strategy for returning results to avoid issues where utterances | |
# like the "the third one" or "the red one" or "the first division book" would miss-recognize as | |
# a numerical index or ordinal as well. | |
locale = options.locale if (options and options.locale) else Culture.English | |
matched = Find.find_choices(utterance, choices_list, options) | |
if not matched: | |
matches = [] | |
if not options or options.recognize_ordinals: | |
# Next try finding by ordinal | |
matches = ChoiceRecognizers._recognize_ordinal(utterance, locale) | |
for match in matches: | |
ChoiceRecognizers._match_choice_by_index( | |
choices_list, matched, match | |
) | |
if not matches and (not options or options.recognize_numbers): | |
# Then try by numerical index | |
matches = ChoiceRecognizers._recognize_number(utterance, locale) | |
for match in matches: | |
ChoiceRecognizers._match_choice_by_index( | |
choices_list, matched, match | |
) | |
# Sort any found matches by their position within the utterance. | |
# - The results from find_choices() are already properly sorted so we just need this | |
# for ordinal & numerical lookups. | |
matched = sorted(matched, key=lambda model_result: model_result.start) | |
return matched | |
def _recognize_ordinal(utterance: str, culture: str) -> List[ModelResult]: | |
model: OrdinalModel = NumberRecognizer(culture).get_ordinal_model(culture) | |
return list( | |
map(ChoiceRecognizers._found_choice_constructor, model.parse(utterance)) | |
) | |
def _match_choice_by_index( | |
choices: List[Choice], matched: List[ModelResult], match: ModelResult | |
): | |
try: | |
index: int = int(match.resolution.value) - 1 | |
if 0 <= index < len(choices): | |
choice = choices[index] | |
matched.append( | |
ModelResult( | |
start=match.start, | |
end=match.end, | |
type_name="choice", | |
text=match.text, | |
resolution=FoundChoice( | |
value=choice.value, index=index, score=1.0 | |
), | |
) | |
) | |
except: | |
# noop here, as in dotnet/node repos | |
pass | |
def _recognize_number(utterance: str, culture: str) -> List[ModelResult]: | |
model: NumberModel = NumberRecognizer(culture).get_number_model(culture) | |
return list( | |
map(ChoiceRecognizers._found_choice_constructor, model.parse(utterance)) | |
) | |
def _found_choice_constructor(value_model: ModelResult) -> ModelResult: | |
return ModelResult( | |
start=value_model.start, | |
end=value_model.end, | |
type_name="choice", | |
text=value_model.text, | |
resolution=FoundChoice( | |
value=value_model.resolution["value"], index=0, score=1.0 | |
), | |
) | |