8000 Adding missing tests · ericmicrofocus/botbuilder-python@99b7d88 · GitHub
[go: up one dir, main page]

Skip to content

Commit 99b7d88

Browse files
committed
Adding missing tests
1 parent 76a470b commit 99b7d88

18 files changed

+4233
-63
lines changed

libraries/botbuilder-ai/botbuilder/ai/luis/luis_recognizer.py

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@
33

44
import json
55
from typing import Dict, List, Tuple, Union
6-
7-
from azure.cognitiveservices.language.luis.runtime import LUISRuntimeClient
8-
from msrest.authentication import CognitiveServicesCredentials
9-
106
from botbuilder.core import (
117
BotAssert,
128
IntentScore,
@@ -15,11 +11,7 @@
1511
TurnContext,
1612
)
1713
from botbuilder.schema import ActivityTypes
18-
1914
from . import LuisApplication, LuisPredictionOptions, LuisTelemetryConstants
20-
21-
from .luis_util import LuisUtil
22-
2315
from .luis_recognizer_v3 import LuisRecognizerV3
2416
from .luis_recognizer_v2 import LuisRecognizerV2
2517
from .luis_recognizer_options_v2 import LuisRecognizerOptionsV2
@@ -65,17 +57,16 @@ def __init__(
6557
)
6658

6759
self._options = prediction_options or LuisPredictionOptions()
68-
69-
self._include_api_results = include_api_results
60+
self._include_api_results = include_api_results or (
61+
prediction_options.include_api_results
62+
if isinstance(
63+
prediction_options, (LuisRecognizerOptionsV3, LuisRecognizerOptionsV2)
64+
)
65+
else False
66+
)
7067

7168
self.telemetry_client = self._options.telemetry_client
7269
self.log_personal_information = self._options.log_personal_information
73-
credentials = CognitiveServicesCredentials(self._application.endpoint_key)
74-
self._runtime = LUISRuntimeClient(self._application.endpoint, credentials)
75-
self._runtime.config.add_user_agent(LuisUtil.get_user_agent())
76-
77-
if isinstance(prediction_options, LuisPredictionOptions):
78-
self._runtime.config.connection.timeout = self._options.timeout // 1000
7970

8071
@staticmethod
8172
def top_intent(

libraries/botbuilder-ai/botbuilder/ai/luis/luis_recognizer_options_v3.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ def __init__(
1111
include_instance_data: bool = True,
1212
log: bool = True,
1313
prefer_external_entities: bool = False,
14+
datetime_reference: str = None,
1415
dynamic_lists: List = None,
1516
external_entities: List = None,
1617
slot: str = "production" or "staging",
@@ -26,6 +27,7 @@ def __init__(
2627
self.include_instance_data = include_instance_data
2728
self.log = log
2829
self.prefer_external_entities = prefer_external_entities
30+
self.datetime_reference = datetime_reference
2931
self.dynamic_lists = dynamic_lists
3032
self.external_entities = external_entities
3133
self.slot = slot

libraries/botbuilder-ai/botbuilder/ai/luis/luis_recognizer_v3.py

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1+
import re
2+
from typing import Dict
3+
14
import aiohttp
5+
from botbuilder.ai.luis.activity_util import ActivityUtil
6+
from botbuilder.ai.luis.luis_util import LuisUtil
27
from botbuilder.core import (
38
IntentScore,
49
RecognizerResult,
@@ -25,6 +30,7 @@ class LuisRecognizerV3(LuisRecognizerInternal):
2530
]
2631
_geographySubtypes = ["poi", "city", "countryRegion", "continent", "state"]
2732
_metadata_key = "$instance"
33+
2834
# The value type for a LUIS trace activity.
2935
luis_trace_type: str = "https://www.luis.ai/schemas/trace"
3036

@@ -67,11 +73,23 @@ async def recognizer_internal(self, turn_context: TurnContext):
6773
),
6874
)
6975

70-
if self.luis_recognizer_options_v3.include_instance_data:
71-
recognizer_result.entities[self._metadata_key] = (
72-
recognizer_result.entities[self._metadata_key]
73-
if recognizer_result.entities[self._metadata_key]
74-
else {}
76+
if self.luis_recognizer_options_v3.include_instance_data:
77+
recognizer_result.entities[self._metadata_key] = (
78+
recognizer_result.entities[self._metadata_key]
79+
if self._metadata_key in recognizer_result.entities
80+
else {}
81+
)
82+
83+
if "sentiment" in luis_result["prediction"]:
84+
recognizer_result.properties["sentiment"] = self._get_sentiment(
85+
luis_result["prediction"]
86+
)
87+
88+
await self._emit_trace_info(
89+
turn_context,
90+
luis_result,
91+
recognizer_result,
92+
self.luis_recognizer_options_v3,
7593
)
7694

7795
return recognizer_result
@@ -104,9 +122,16 @@ def _build_url(self):
104122
def _build_request(self, utterance: str):
105123
body = {
106124
"query": utterance,
107-
"preferExternalEntities": self.luis_recognizer_options_v3.prefer_external_entities,
125+
"options": {
126+
"preferExternalEntities": self.luis_recognizer_options_v3.prefer_external_entities,
127+
},
108128
}
109129

130+
if self.luis_recognizer_options_v3.datetime_reference:
131+
body["options"][
132+
"datetimeReference"
133+
] = self.luis_recognizer_options_v3.datetime_reference
134+
110135
if self.luis_recognizer_options_v3.dynamic_lists:
111136
body["dynamicLists"] = self.luis_recognizer_options_v3.dynamic_lists
112137

@@ -121,14 +146,19 @@ def _get_intents(self, luis_result):
121146
return intents
122147

123148
for intent in luis_result["intents"]:
124-
intents[intent] = IntentScore(luis_result["intents"][intent]["score"])
149+
intents[self._normalize_name(intent)] = IntentScore(
150+
luis_result["intents"][intent]["score"]
151+
)
125152

126153
return intents
127154

155+
def _normalize_name(self, name):
156+
return re.sub(r"\.", "_", name)
157+
128158
def _normalize(self, entity):
129159
split_entity = entity.split(":")
130160
entity_name = split_entity[-1]
131-
return entity_name
161+
return self._normalize_name(entity_name)
132162

133163
def _extract_entities_and_metadata(self, luis_result):
134164
entities = luis_result["entities"]
@@ -219,3 +249,33 @@ def _map_properties(self, source, in_instance):
219249

220250
result = nobj
221251
return result
252+
253+
def _get_sentiment(self, luis_result):
254+
return {
255+
"label": luis_result["sentiment"]["label"],
256+
"score": luis_result["sentiment"]["score"],
257+
}
258+
259+
async def _emit_trace_info(
260+
self,
261+
turn_context: TurnContext,
262+
luis_result,
263+
recognizer_result: RecognizerResult,
264+
options: LuisRecognizerOptionsV3,
265+
) -> None:
266+
trace_info: Dict[str, object] = {
267+
"recognizerResult": LuisUtil.recognizer_result_as_dict(recognizer_result),
268+
"luisModel": {"ModelID": self._application.application_id},
269+
"luisOptions": {"Slot": options.slot},
270+
"luisResult": luis_result,
271+
}
272+
273+
trace_activity = ActivityUtil.create_trace(
274+
turn_context.activity,
275+
"LuisRecognizer",
276+
trace_info,
277+
LuisRecognizerV3.luis_trace_type,
278+
LuisRecognizerV3.luis_trace_label,
279+
)
280+
281+
await turn_context.send_activity(trace_activity)

libraries/botbuilder-ai/tests/luis/luis_recognizer_test.py

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -70,21 +70,6 @@ def test_luis_recognizer_construction(self):
7070
self.assertEqual("048ec46dc58e495482b0c447cfdbd291", app.endpoint_key)
7171
self.assertEqual("https://westus.api.cognitive.microsoft.com", app.endpoint)
7272

73-
def test_luis_recognizer_timeout(self):
74-
endpoint = (
75-
"https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/"
76-
"b31aeaf3-3511-495b-a07f-571fc873214b?verbose=true&timezoneOffset=-360"
77-
"&subscription-key=048ec46dc58e495482b0c447cfdbd291&q="
78-
)
79-
expected_timeout = 300
80-
options_with_timeout = LuisPredictionOptions(timeout=expected_timeout * 1000)
81-
82-
recognizer_with_timeout = LuisRecognizer(endpoint, options_with_timeout)
83-
84-
self.assertEqual(
85-
expected_timeout, recognizer_with_timeout._runtime.config.connection.timeout
86-
)
87-
8873
def test_none_endpoint(self):
8974
# Arrange
9075
my_app = LuisApplication(
@@ -418,24 +403,6 @@ def test_top_intent_returns_top_intent_if_score_equals_min_score(self):
418403
)
419404
self.assertEqual(default_intent, "Greeting")
420405

421-
async def test_user_agent_contains_product_version(self):
422-
utterance: str = "please book from May 5 to June 6"
423-
response_path: str = "MultipleDateTimeEntities.json" # it doesn't matter to use which file.
424-
425-
recognizer, _ = await LuisRecognizerTest._get_recognizer_result(
426-
utterance, response_path, bot_adapter=NullAdapter()
427-
)
428-
429-
runtime: LUISRuntimeClient = recognizer._runtime
430-
config: LUISRuntimeClientConfiguration = runtime.config
431-
user_agent = config.user_agent
432-
433-
# Verify we didn't unintentionally stamp on the user-agent from the client.
434-
self.assertTrue("azure-cognitiveservices-language-luis" in user_agent)
435-
436-
# And that we added the bot.builder package details.
437-
self.assertTrue("botbuilder-ai/4" in user_agent)
438-
439406
def test_telemetry_construction(self):
440407
# Arrange
441408
# Note this is NOT a real LUIS application ID nor a real LUIS subscription-key

libraries/botbuilder-ai/tests/luis/luis_recognizer_v3_test.py

Lines changed: 86 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@
77
from os import path
88
from typing import Dict, Tuple, Union
99

10-
from aiounittest import AsyncTestCase
10+
import re
11+
from aioresponses import aioresponses
1112

13+
from aiounittest import AsyncTestCase
14+
from unittest import mock
15+
from unittest.mock import MagicMock, Mock
1216
from botbuilder.ai.luis import LuisRecognizerOptionsV3
1317

1418
from asynctest import CoroutineMock, patch
@@ -54,7 +58,8 @@ def _remove_none_property(dictionary: Dict[str, object]) -> Dict[str, object]:
5458
return dictionary
5559

5660
@classmethod
57-
@patch('aiohttp.ClientSession.post')
61+
# @patch('aiohttp.ClientSession.post')
62+
@aioresponses()
5863
async def _get_recognizer_result(
5964
cls,
6065
utterance: str,
@@ -77,7 +82,10 @@ async def _get_recognizer_result(
7782
recognizer_class, include_api_results=include_api_results, options=options
7883
)
7984
context = LuisRecognizerV3Test._get_context(utterance, bot_adapter)
80-
mock_get.return_value.__aenter__.return_value.json = CoroutineMock(side_effect=[response_json])
85+
# mock_get.return_value.__aenter__.return_value.json = CoroutineMock(side_effect=[response_json])
86+
87+
pattern = re.compile(r'^https://westus.api.cognitive.microsoft.com.*$')
88+
mock_get.post(pattern, payload=response_json, status=200)
8189

8290
result = await recognizer.recognize(
8391
context, telemetry_properties, telemetry_metrics
@@ -146,6 +154,10 @@ async def _test_json_v3(self, response_file: str) -> None:
146154
if "version" in test_options:
147155
options.version=test_options["version"]
148156

157+
if "externalEntities" in test_options:
158+
options.external_entities=test_options["externalEntities"]
159+
160+
149161
# dynamic_lists: List = None,
150162
# external_entities: List = None,
151163
# telemetry_client: BotTelemetryClient = NullTelemetryClient(),
@@ -160,9 +172,9 @@ async def _test_json_v3(self, response_file: str) -> None:
160172
# Assert
161173
actual_result_json = LuisUtil.recognizer_result_as_dict(result)
162174
del expected_json["v3"]
163-
del expected_json["sentiment"]
164175
trimmed_expected = LuisRecognizerV3Test._remove_none_property(expected_json)
165176
trimmed_actual = LuisRecognizerV3Test._remove_none_property(actual_result_json)
177+
166178
self.assertEqual(trimmed_expected, trimmed_actual)
167179

168180
async def test_composite1_v3(self):
@@ -172,4 +184,73 @@ async def test_composite2_v3(self):
172184
await self._test_json_v3("Composite2_v3.json")
173185

174186
async def test_composite3_v3(self):
175-
await self._test_json_v3("Composite3_v3.json")
187+
await self._test_json_v3("Composite3_v3.json")
188+
189+
async def test_external_entities_and_built_in_v3(self):
190+
await self._test_json_v3("ExternalEntitiesAndBuiltIn_v3.json")
191+
192+
async def test_external_entities_and_composite_v3(self):
193+
await self._test_json_v3("ExternalEntitiesAndComposite_v3.json")
194+
195+
async def test_external_entities_and_list_v3(self):
196+
await self._test_json_v3("ExternalEntitiesAndList_v3.json")
197+
198+
async def test_external_entities_and_regex_v3(self):
199+
await self._test_json_v3("ExternalEntitiesAndRegex_v3.json")
200+
201+
async def test_external_entities_and_simple_v3(self):
202+
await self._test_json_v3("ExternalEntitiesAndSimple_v3.json")
203+
204+
async def test_geo_people_ordinal_v3(self):
205+
await self._test_json_v3("GeoPeopleOrdinal_v3.json")
206+
207+
async def test_minimal_v3(self):
208+
await self._test_json_v3("Minimal_v3.json")
209+
210+
async def test_no_entities_instance_true_v3(self):
211+
await self._test_json_v3("NoEntitiesInstanceTrue_v3.json")
212+
213+
async def test_patterns_v3(self):
214+
await self._test_json_v3("Patterns_v3.json")
215+
216+
async def test_prebuilt_v3(self):
217+
await self._test_json_v3("Prebuilt_v3.json")
218+
219+
async def test_roles_v3(self):
220+
await self._test_json_v3("roles_v3.json")
221+
222+
async def test_trace_activity(self):
223+
# Arrange
224+
utterance: str = "fly on delta at 3pm"
225+
expected_json = LuisRecognizerV3Test._get_json_for_file("Minimal_v3.json")
226+
response_json = expected_json["v3"]["response"]
227+
228+
# add async support to magic mock.
229+
async def async_magic():
230+
pass
231+
232+
MagicMock.__await__ = lambda x: async_magic().__await__()
233+
234+
# Act
235+
with mock.patch.object(TurnContext, "send_activity") as mock_send_activity:
236+
await self._get_recognizer_result(utterance, response_json, options= LuisRecognizerOptionsV3())
237+
trace_activity: Activity = mock_send_activity.call_args[0][0]
238+
239+
# Assert
240+
self.assertIsNotNone(trace_activity)
241+
self.assertEqual(LuisRecognizer.luis_trace_type, trace_activity.value_type)
242+
self.assertEqual(LuisRecognizer.luis_trace_label, trace_activity.label)
243+
244+
luis_trace_info = trace_activity.value
245+
self.assertIsNotNone(luis_trace_info)
246+
self.assertIsNotNone(luis_trace_info["recognizerResult"])
247+
self.assertIsNotNone(luis_trace_info["luisResult"])
248+
self.assertIsNotNone(luis_trace_info["luisOptions"])
249+
self.assertIsNotNone(luis_trace_info["luisModel"])
250+
251+
recognizer_result: RecognizerResult = luis_trace_info["recognizerResult"]
252+
self.assertEqual(utterance, recognizer_result["text"])
253+
self.assertIsNotNone(recognizer_result["intents"]["Roles"])
254+
self.assertEqual(
255+
LuisRecognizerV3Test._luisAppId, luis_trace_info["luisModel"]["ModelID"]
256+
)

libraries/botbuilder-ai/tests/luis/test_data/Composite2_v3.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@
295295
"Travel": {
296296
"score": 0.0154484725
297297
},
298-
"Weather_GetForecast": {
298+
"Weather.GetForecast": {
299299
"score": 0.0237181056
300300
}
301301
},

0 commit comments

Comments
 (0)
0