8000 Add FindChoiceOptions for recognize_numbers and recognize_ordinals (#… · TheCompuGuru/botbuilder-python@ec836cf · GitHub
[go: up one dir, main page]

Skip to content

Commit ec836cf

Browse files
authored
Add FindChoiceOptions for recognize_numbers and recognize_ordinals (microsoft#691)
* Add FindChoiceOptions for recognize_numbers and recognize_ordinals * Corrected if statement
1 parent 2127e7f commit ec836cf

File tree

3 files changed

+896
-858
lines changed

3 files changed

+896
-858
lines changed
Lines changed: 141 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -1,140 +1,141 @@
1-
# Copyright (c) Microsoft Corporation. All rights reserved.
2-
# Licensed under the MIT License.
3-
4-
from typing import List, Union
5-
from recognizers_number import NumberModel, NumberRecognizer, OrdinalModel
6-
from recognizers_text import Culture
7-
8-
9-
from .choice import Choice
10-
from .find import Find
11-
from .find_choices_options import FindChoicesOptions
12-
from .found_choice import FoundChoice
13-
from .model_result import ModelResult
14-
15-
16-
class ChoiceRecognizers:
17-
""" Contains methods for matching user input against a list of choices. """
18-
19-
@staticmethod
20-
def recognize_choices(
21-
utterance: str,
22-
choices: List[Union[str, Choice]],
23-
options: FindChoicesOptions = None,
24-
) -> List[ModelResult]:
25-
"""
26-
Matches user input against a list of choices.
27-
28-
This is layered above the `Find.find_choices()` function, and adds logic to let the user specify
29-
their choice by index (they can say "one" to pick `choice[0]`) or ordinal position
30-
(they can say "the second one" to pick `choice[1]`.)
31-
The user's utterance is recognized in the following order:
32-
33-
- By name using `find_choices()`
34-
- By 1's based ordinal position.
35-
- By 1's based index position.
36-
37-
Parameters:
38-
-----------
39-
40-
utterance: The input.
41-
42-
choices: The list of choices.
43-
44-
options: (Optional) Options to control the recognition strategy.
45-
46-
Returns:
47-
--------
48-
A list of found choices, sorted by most relevant first.
49-
"""
50-
if utterance is None:
51-
utterance = ""
52-
53-
# Normalize list of choices
54-
choices_list = [
55-
Choice(value=choice) if isinstance(choice, str) else choice
56-
for choice in choices
57-
]
58-
59-
# Try finding choices by text search first
60-
# - We only want to use a single strategy for returning results to avoid issues where utterances
61-
# like the "the third one" or "the red one" or "the first division book" would miss-recognize as
62-
# a numerical index or ordinal as well.
63-
locale = options.locale if (options and options.locale) else Culture.English
64-
matched = Find.find_choices(utterance, choices_list, options)
65-
if not matched:
66-
# Next try finding by ordinal
67-
matches = ChoiceRecognizers._recognize_ordinal(utterance, locale)
68-
69-
if matches:
70-
for match in matches:
71-
ChoiceRecognizers._match_choice_by_index(
72-
choices_list, matched, match
73-
)
74-
else:
75-
# Finally try by numerical index
76-
matches = ChoiceRecognizers._recognize_number(utterance, locale)
77-
78-
for match in matches:
79-
ChoiceRecognizers._match_choice_by_index(
80-
choices_list, matched, match
81-
)
82-
83-
# Sort any found matches by their position within the utterance.
84-
# - The results from find_choices() are already properly sorted so we just need this
85-
# for ordinal & numerical lookups.
86-
matched = sorted(matched, key=lambda model_result: model_result.start)
87-
88-
return matched
89-
90-
@staticmethod
91-
def _recognize_ordinal(utterance: str, culture: str) -> List[ModelResult]:
92-
model: OrdinalModel = NumberRecognizer(culture).get_ordinal_model(culture)
93-
94-
return list(
95-
map(ChoiceRecognizers._found_choice_constructor, model.parse(utterance))
96-
)
97-
98-
@staticmethod
99-
def _match_choice_by_index(
100-
choices: List[Choice], matched: List[ModelResult], match: ModelResult
101-
):
102-
try:
103-
index: int = int(match.resolution.value) - 1
104-
if 0 <= index < len(choices):
105-
choice = choices[index]
106-
107-
matched.append(
108-
ModelResult(
109-
start=match.start,
110-
end=match.end,
111-
type_name="choice",
112-
text=match.text,
113-
resolution=FoundChoice(
114-
value=choice.value, index=index, score=1.0
115-
),
116-
)
117-
)
118-
except:
119-
# noop here, as in dotnet/node repos
120-
pass
121-
122-
@staticmethod
123-
def _recognize_number(utterance: str, culture: str) -> List[ModelResult]:
124-
model: NumberModel = NumberRecognizer(culture).get_number_model(culture)
125-
126-
return list(
127-
map(ChoiceRecognizers._found_choice_constructor, model.parse(utterance))
128-
)
129-
130-
@staticmethod
131-
def _found_choice_constructor(value_model: ModelResult) -> ModelResult:
132-
return ModelResult(
133-
start=value_model.start,
134-
end=value_model.end,
135-
type_name="choice",
136-
text=value_model.text,
137-
resolution=FoundChoice(
138-
value=value_model.resolution["value"], index=0, score=1.0
139-
),
140-
)
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from typing import List, Union
5+
from recognizers_number import NumberModel, NumberRecognizer, OrdinalModel
6+
from recognizers_text import Culture
7+
8+
9+
from .choice import Choice
10+
from .find import Find
11+
from .find_choices_options import FindChoicesOptions
12+
from .found_choice import FoundChoice
13+
from .model_result import ModelResult
14+
15+
16+
class ChoiceRecognizers:
17+
""" Contains methods for matching user input against a list of choices. """
18+
19+
@staticmethod
20+
def recognize_choices(
21+
utterance: str,
22+
choices: List[Union[str, Choice]],
23+
options: FindChoicesOptions = None,
24+
) -> List[ModelResult]:
25+
"""
26+
Matches user input against a list of choices.
27+
28+
This is layered above the `Find.find_choices()` function, and adds logic to let the user specify
29+
their choice by index (they can say "one" to pick `choice[0]`) or ordinal position
30+
(they can say "the second one" to pick `choice[1]`.)
31+
The user's utterance is recognized in the following order:
32+
33+
- By name using `find_choices()`
34+
- By 1's based ordinal position.
35+
- By 1's based index position.
36+
37+
Parameters:
38+
-----------
39+
40+
utterance: The input.
41+
42+
choices: The list of choices.
43+
44+
options: (Optional) Options to control the recognition strategy.
45+
46+
Returns:
47+
--------
48+
A list of found choices, sorted by most relevant first.
49+
"""
50+
if utterance is None:
51+
utterance = ""
52+
53+
# Normalize list of choices
54+
choices_list = [
55+
Choice(value=choice) if isinstance(choice, str) else choice
56+
for choice in choices
57+
]
58+
59+
# Try finding choices by text search first
60+
# - We only want to use a single strategy for returning results to avoid issues where utterances
61+
# like the "the third one" or "the red one" or "the first division book" would miss-recognize as
62+
# a numerical index or ordinal as well.
63+
locale = options.locale if (options and options.locale) else Culture.English
64+
matched = Find.find_choices(utterance, choices_list, options)
65+
if not matched:
66+
matches = []
67+
68+
if not options or options.recognize_ordinals:
69+
# Next try finding by ordinal
70+
matches = ChoiceRecognizers._recognize_ordinal(utterance, locale)
71+
for match in matches:
72+
ChoiceRecognizers._match_choice_by_index(
73+
choices_list, matched, match
74+
)
75+
76+
if not matches and (not options or options.recognize_numbers):
77+
# Then try by numerical index
78+
matches = ChoiceRecognizers._recognize_number(utterance, locale)
79+
for match in matches:
80+
ChoiceRecognizers._match_choice_by_index(
81+
choices_list, matched, match
82+
)
83+
84+
# Sort any found matches by their position within the utterance.
85+
# - The results from find_choices() are already properly sorted so we just need this
86+
# for ordinal & numerical lookups.
87+
matched = sorted(matched, key=lambda model_result: model_result.start)
88+
89+
return matched
90+
91+
@staticmethod
92+
def _recognize_ordinal(utterance: str, culture: str) -> List[ModelResult]:
93+
model: OrdinalModel = NumberRecognizer(culture).get_ordinal_model(culture)
94+
95+
return list(
96+
map(ChoiceRecognizers._found_choice_constructor, model.parse(utterance))
97+
)
98+
99+
@staticmethod
100+
def _match_choice_by_index(
101+
choices: List[Choice], matched: List[ModelResult], match: ModelResult
102+
):
103+
try:
104+
index: int = int(match.resolution.value) - 1
105+
if 0 <= index < len(choices):
106+
choice = choices[index]
107+
108+
matched.append(
109+
ModelResult(
110+
start=match.start,
111+
end=match.end,
112+
type_name="choice",
113+
text=match.text,
114+
resolution=FoundChoice(
115+
value=choice.value, index=index, score=1.0
116+
),
117+
)
118+
)
119+
except:
120+
# noop here, as in dotnet/node repos
121+
pass
122+
123+
@staticmethod
124+
def _recognize_number(utterance: str, culture: str) -> List[ModelResult]:
125+
model: NumberModel = NumberRecognizer(culture).get_number_model(culture)
126+
127+
return list(
128+
map(ChoiceRecognizers._found_choice_constructor, model.parse(utterance))
129+
)
130+
131+
@staticmethod
132+
def _found_choice_constructor(value_model: ModelResult) -> ModelResult:
133+
return ModelResult(
134+
start=value_model.start,
135+
end=value_model.end,
136+
type_name="choice",
137+
text=value_model.text,
138+
resolution=FoundChoice(
139+
value=value_model.resolution["value"], index=0, score=1.0
140+
),
141+
)
Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,38 @@
1-
# Copyright (c) Microsoft Corporation. All rights reserved.
2-
# Licensed under the MIT License.
3-
4-
from .find_values_options import FindValuesOptions
5-
6-
7-
class FindChoicesOptions(FindValuesOptions):
8-
""" Contains options to control how input is matched against a list of choices """
9-
10-
def __init__(self, no_value: bool = None, no_action: bool = None, **kwargs):
11-
"""
12-
Parameters:
13-
-----------
14-
15-
no_value: (Optional) If `True`, the choices `value` field will NOT be search over. Defaults to `False`.
16-
17-
no_action: (Optional) If `True`, the choices `action.title` field will NOT be searched over.
18-
Defaults to `False`.
19-
"""
20-
21-
super().__init__(**kwargs)
22-
self.no_value = no_value
23-
self.no_action = no_action
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from .find_values_options import FindValuesOptions
5+
6+
7+
class FindChoicesOptions(FindValuesOptions):
8+
""" Contains options to control how input is matched against a list of choices """
9+
10+
def __init__(
11+
self,
12+
no_value: bool = None,
13+
no_action: bool = None,
14+
recognize_numbers: bool = True,
15+
recognize_ordinals: bool = True,
16+
< AF3E span class=pl-c1>**kwargs,
17+
):
18+
"""
19+
Parameters:
20+
-----------
21+
22+
no_value: (Optional) If `True`, the choices `value` field will NOT be search over. Defaults to `False`.
23+
24+
no_action: (Optional) If `True`, the choices `action.title` field will NOT be searched over.
25+
Defaults to `False`.
26+
27+
recognize_numbers: (Optional) Indicates whether the recognizer should check for Numbers using the
28+
NumberRecognizer's NumberModel.
29+
30+
recognize_ordinals: (Options) Indicates whether the recognizer should check for Ordinal Numbers using
31+
the NumberRecognizer's OrdinalModel.
32+
"""
33+
34+
super().__init__(**kwargs)
35+
self.no_value = no_value
36+
self.no_action = no_action
37+
self.recognize_numbers = recognize_numbers
38+
self.recognize_ordinals = recognize_ordinals

0 commit comments

Comments
 (0)
0