Spaces:
Build error
Build error
Validify-testbot-1
/
botbuilder-python
/libraries
/botbuilder-ai
/tests
/luis
/luis_recognizer_test.py
# Copyright (c) Microsoft Corporation. All rights reserved. | |
# Licensed under the MIT License. | |
# pylint: disable=protected-access | |
import json | |
from os import path | |
from typing import Dict, Tuple, Union | |
from unittest import mock | |
from unittest.mock import MagicMock, Mock | |
from aiounittest import AsyncTestCase | |
from msrest import Deserializer | |
from requests import Session | |
from requests.models import Response | |
from botbuilder.ai.luis import LuisApplication, LuisPredictionOptions, LuisRecognizer | |
from botbuilder.ai.luis.luis_util import LuisUtil | |
from botbuilder.core import ( | |
BotAdapter, | |
BotTelemetryClient, | |
IntentScore, | |
RecognizerResult, | |
TurnContext, | |
) | |
from botbuilder.core.adapters import TestAdapter | |
from botbuilder.schema import ( | |
Activity, | |
ActivityTypes, | |
ChannelAccount, | |
ConversationAccount, | |
) | |
from null_adapter import NullAdapter | |
from override_fill_recognizer import OverrideFillRecognizer | |
from telemetry_override_recognizer import TelemetryOverrideRecognizer | |
class LuisRecognizerTest(AsyncTestCase): | |
_luisAppId: str = "b31aeaf3-3511-495b-a07f-571fc873214b" | |
_subscriptionKey: str = "048ec46dc58e495482b0c447cfdbd291" | |
_endpoint: str = "https://westus.api.cognitive.microsoft.com" | |
def __init__(self, *args, **kwargs): | |
super(LuisRecognizerTest, self).__init__(*args, **kwargs) | |
self._mocked_results: RecognizerResult = RecognizerResult( | |
intents={"Test": IntentScore(score=0.2), "Greeting": IntentScore(score=0.4)} | |
) | |
self._empty_luis_response: Dict[str, object] = json.loads( | |
'{ "query": null, "intents": [], "entities": [] }' | |
) | |
def test_luis_recognizer_construction(self): | |
# Arrange | |
endpoint = ( | |
"https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/" | |
"b31aeaf3-3511-495b-a07f-571fc873214b?verbose=true&timezoneOffset=-360" | |
"&subscription-key=048ec46dc58e495482b0c447cfdbd291&q=" | |
) | |
# Act | |
recognizer = LuisRecognizer(endpoint) | |
# Assert | |
app = recognizer._application | |
self.assertEqual("b31aeaf3-3511-495b-a07f-571fc873214b", app.application_id) | |
self.assertEqual("048ec46dc58e495482b0c447cfdbd291", app.endpoint_key) | |
self.assertEqual("https://westus.api.cognitive.microsoft.com", app.endpoint) | |
def test_none_endpoint(self): | |
# Arrange | |
my_app = LuisApplication( | |
LuisRecognizerTest._luisAppId, | |
LuisRecognizerTest._subscriptionKey, | |
endpoint=None, | |
) | |
# Assert | |
recognizer = LuisRecognizer(my_app, prediction_options=None) | |
# Assert | |
app = recognizer._application | |
self.assertEqual("https://westus.api.cognitive.microsoft.com", app.endpoint) | |
def test_empty_endpoint(self): | |
# Arrange | |
my_app = LuisApplication( | |
LuisRecognizerTest._luisAppId, | |
LuisRecognizerTest._subscriptionKey, | |
endpoint="", | |
) | |
# Assert | |
recognizer = LuisRecognizer(my_app, prediction_options=None) | |
# Assert | |
app = recognizer._application | |
self.assertEqual("https://westus.api.cognitive.microsoft.com", app.endpoint) | |
def test_luis_recognizer_none_luis_app_arg(self): | |
with self.assertRaises(TypeError): | |
LuisRecognizer(application=None) | |
async def test_single_intent_simply_entity(self): | |
utterance: str = "My name is Emad" | |
response_path: str = "SingleIntent_SimplyEntity.json" | |
_, result = await LuisRecognizerTest._get_recognizer_result( | |
utterance, response_path | |
) | |
self.assertIsNotNone(result) | |
self.assertIsNone(result.altered_text) | |
self.assertEqual(utterance, result.text) | |
self.assertIsNotNone(result.intents) | |
self.assertEqual(1, len(result.intents)) | |
self.assertIsNotNone(result.intents["SpecifyName"]) | |
self.assert_score(result.intents["SpecifyName"].score) | |
self.assertIsNotNone(result.entities) | |
self.assertIsNotNone(result.entities["Name"]) | |
self.assertEqual("emad", result.entities["Name"][0]) | |
self.assertIsNotNone(result.entities["$instance"]) | |
self.assertIsNotNone(result.entities["$instance"]["Name"]) | |
self.assertEqual(11, result.entities["$instance"]["Name"][0]["startIndex"]) | |
self.assertEqual(15, result.entities["$instance"]["Name"][0]["endIndex"]) | |
self.assert_score(result.entities["$instance"]["Name"][0]["score"]) | |
async def test_null_utterance(self): | |
utterance: str = None | |
response_path: str = ( | |
"SingleIntent_SimplyEntity.json" # The path is irrelevant in this case | |
) | |
_, result = await LuisRecognizerTest._get_recognizer_result( | |
utterance, response_path | |
) | |
self.assertIsNotNone(result) | |
self.assertIsNone(result.altered_text) | |
self.assertEqual(utterance, result.text) | |
self.assertIsNotNone(result.intents) | |
self.assertEqual(1, len(result.intents)) | |
self.assertIsNotNone(result.intents[""]) | |
self.assertEqual(result.get_top_scoring_intent(), ("", 1.0)) | |
self.assertIsNotNone(result.entities) | |
self.assertEqual(0, len(result.entities)) | |
async def test_multiple_intents_prebuilt_entity(self): | |
utterance: str = "Please deliver February 2nd 2001" | |
response_path: str = "MultipleIntents_PrebuiltEntity.json" | |
_, result = await LuisRecognizerTest._get_recognizer_result( | |
utterance, response_path | |
) | |
self.assertIsNotNone(result) | |
self.assertEqual(utterance, result.text) | |
self.assertIsNotNone(result.intents) | |
self.assertTrue(len(result.intents) > 1) | |
self.assertIsNotNone(result.intents["Delivery"]) | |
self.assertTrue( | |
result.intents["Delivery"].score > 0 | |
and result.intents["Delivery"].score <= 1 | |
) | |
self.assertEqual("Delivery", result.get_top_scoring_intent().intent) | |
self.assertTrue(result.get_top_scoring_intent().score > 0) | |
self.assertIsNotNone(result.entities) | |
self.assertIsNotNone(result.entities["number"]) | |
self.assertEqual(2001, int(result.entities["number"][0])) | |
self.assertIsNotNone(result.entities["ordinal"]) | |
self.assertEqual(2, int(result.entities["ordinal"][0])) | |
self.assertIsNotNone(result.entities["datetime"][0]) | |
self.assertEqual("2001-02-02", result.entities["datetime"][0]["timex"][0]) | |
self.assertIsNotNone(result.entities["$instance"]["number"]) | |
self.assertEqual( | |
28, int(result.entities["$instance"]["number"][0]["startIndex"]) | |
) | |
self.assertEqual(32, int(result.entities["$instance"]["number"][0]["endIndex"])) | |
self.assertEqual("2001", result.text[28:32]) | |
self.assertIsNotNone(result.entities["$instance"]["datetime"]) | |
self.assertEqual(15, result.entities["$instance"]["datetime"][0]["startIndex"]) | |
self.assertEqual(32, result.entities["$instance"]["datetime"][0]["endIndex"]) | |
self.assertEqual( | |
"february 2nd 2001", result.entities["$instance"]["datetime"][0]["text"] | |
) | |
async def test_multiple_intents_prebuilt_entities_with_multi_values(self): | |
utterance: str = "Please deliver February 2nd 2001 in room 201" | |
response_path: str = "MultipleIntents_PrebuiltEntitiesWithMultiValues.json" | |
_, result = await LuisRecognizerTest._get_recognizer_result( | |
utterance, response_path | |
) | |
self.assertIsNotNone(result) | |
self.assertIsNotNone(result.text) | |
self.assertEqual(utterance, result.text) | |
self.assertIsNotNone(result.intents) | |
self.assertIsNotNone(result.intents["Delivery"]) | |
self.assertIsNotNone(result.entities) | |
self.assertIsNotNone(result.entities["number"]) | |
self.assertEqual(2, len(result.entities["number"])) | |
self.assertTrue(201 in map(int, result.entities["number"])) | |
self.assertTrue(2001 in map(int, result.entities["number"])) | |
self.assertIsNotNone(result.entities["datetime"][0]) | |
self.assertEqual("2001-02-02", result.entities["datetime"][0]["timex"][0]) | |
async def test_multiple_intents_list_entity_with_single_value(self): | |
utterance: str = "I want to travel on united" | |
response_path: str = "MultipleIntents_ListEntityWithSingleValue.json" | |
_, result = await LuisRecognizerTest._get_recognizer_result( | |
utterance, response_path | |
) | |
self.assertIsNotNone(result) | |
self.assertIsNotNone(result.text) | |
self.assertEqual(utterance, result.text) | |
self.assertIsNotNone(result.intents) | |
self.assertIsNotNone(result.intents["Travel"]) | |
self.assertIsNotNone(result.entities) | |
self.assertIsNotNone(result.entities["Airline"]) | |
self.assertEqual("United", result.entities["Airline"][0][0]) | |
self.assertIsNotNone(result.entities["$instance"]) | |
self.assertIsNotNone(result.entities["$instance"]["Airline"]) | |
self.assertEqual(20, result.entities["$instance"]["Airline"][0]["startIndex"]) | |
self.assertEqual(26, result.entities["$instance"]["Airline"][0]["endIndex"]) | |
self.assertEqual("united", result.entities["$instance"]["Airline"][0]["text"]) | |
async def test_multiple_intents_list_entity_with_multi_values(self): | |
utterance: str = "I want to travel on DL" | |
response_path: str = "MultipleIntents_ListEntityWithMultiValues.json" | |
_, result = await LuisRecognizerTest._get_recognizer_result( | |
utterance, response_path | |
) | |
self.assertIsNotNone(result) | |
self.assertIsNotNone(result.text) | |
self.assertEqual(utterance, result.text) | |
self.assertIsNotNone(result.intents) | |
self.assertIsNotNone(result.intents["Travel"]) | |
self.assertIsNotNone(result.entities) | |
self.assertIsNotNone(result.entities["Airline"]) | |
self.assertEqual(2, len(result.entities["Airline"][0])) | |
self.assertTrue("Delta" in result.entities["Airline"][0]) | |
self.assertTrue("Virgin" in result.entities["Airline"][0]) | |
self.assertIsNotNone(result.entities["$instance"]) | |
self.assertIsNotNone(result.entities["$instance"]["Airline"]) | |
self.assertEqual(20, result.entities["$instance"]["Airline"][0]["startIndex"]) | |
self.assertEqual(22, result.entities["$instance"]["Airline"][0]["endIndex"]) | |
self.assertEqual("dl", result.entities["$instance"]["Airline"][0]["text"]) | |
async def test_multiple_intents_composite_entity_model(self): | |
utterance: str = "Please deliver it to 98033 WA" | |
response_path: str = "MultipleIntents_CompositeEntityModel.json" | |
_, result = await LuisRecognizerTest._get_recognizer_result( | |
utterance, response_path | |
) | |
self.assertIsNotNone(result) | |
self.assertIsNotNone(result.text) | |
self.assertEqual(utterance, result.text) | |
self.assertIsNotNone(result.intents) | |
self.assertIsNotNone(result.intents["Delivery"]) | |
self.assertIsNotNone(result.entities) | |
self.assertIsNone(result.entities.get("number")) | |
self.assertIsNone(result.entities.get("State")) | |
self.assertIsNotNone(result.entities["Address"]) | |
self.assertEqual(98033, result.entities["Address"][0]["number"][0]) | |
self.assertEqual("wa", result.entities["Address"][0]["State"][0]) | |
self.assertIsNotNone(result.entities["$instance"]) | |
self.assertIsNone(result.entities["$instance"].get("number")) | |
self.assertIsNone(result.entities["$instance"].get("State")) | |
self.assertIsNotNone(result.entities["$instance"]["Address"]) | |
self.assertEqual(21, result.entities["$instance"]["Address"][0]["startIndex"]) | |
self.assertEqual(29, result.entities["$instance"]["Address"][0]["endIndex"]) | |
self.assert_score(result.entities["$instance"]["Address"][0]["score"]) | |
self.assertIsNotNone(result.entities["Address"][0]["$instance"]) | |
self.assertIsNotNone(result.entities["Address"][0]["$instance"]["number"]) | |
self.assertEqual( | |
21, result.entities["Address"][0]["$instance"]["number"][0]["startIndex"] | |
) | |
self.assertEqual( | |
26, result.entities["Address"][0]["$instance"]["number"][0]["endIndex"] | |
) | |
self.assertEqual( | |
"98033", result.entities["Address"][0]["$instance"]["number"][0]["text"] | |
) | |
self.assertIsNotNone(result.entities["Address"][0]["$instance"]["State"]) | |
self.assertEqual( | |
27, result.entities["Address"][0]["$instance"]["State"][0]["startIndex"] | |
) | |
self.assertEqual( | |
29, result.entities["Address"][0]["$instance"]["State"][0]["endIndex"] | |
) | |
self.assertEqual( | |
"wa", result.entities["Address"][0]["$instance"]["State"][0]["text"] | |
) | |
self.assertEqual("WA", result.text[27:29]) | |
self.assert_score( | |
result.entities["Address"][0]["$instance"]["State"][0]["score"] | |
) | |
async def test_multiple_date_time_entities(self): | |
utterance: str = "Book a table on Friday or tomorrow at 5 or tomorrow at 4" | |
response_path: str = "MultipleDateTimeEntities.json" | |
_, result = await LuisRecognizerTest._get_recognizer_result( | |
utterance, response_path | |
) | |
self.assertIsNotNone(result.entities["datetime"]) | |
self.assertEqual(3, len(result.entities["datetime"])) | |
self.assertEqual(1, len(result.entities["datetime"][0]["timex"])) | |
self.assertEqual("XXXX-WXX-5", result.entities["datetime"][0]["timex"][0]) | |
self.assertEqual(1, len(result.entities["datetime"][0]["timex"])) | |
self.assertEqual(2, len(result.entities["datetime"][1]["timex"])) | |
self.assertEqual(2, len(result.entities["datetime"][2]["timex"])) | |
self.assertTrue(result.entities["datetime"][1]["timex"][0].endswith("T05")) | |
self.assertTrue(result.entities["datetime"][1]["timex"][1].endswith("T17")) | |
self.assertTrue(result.entities["datetime"][2]["timex"][0].endswith("T04")) | |
self.assertTrue(result.entities["datetime"][2]["timex"][1].endswith("T16")) | |
self.assertEqual(3, len(result.entities["$instance"]["datetime"])) | |
async def test_v1_datetime_resolution(self): | |
utterance: str = "at 4" | |
response_path: str = "V1DatetimeResolution.json" | |
_, result = await LuisRecognizerTest._get_recognizer_result( | |
utterance, response_path | |
) | |
self.assertIsNotNone(result.entities["datetime_time"]) | |
self.assertEqual(1, len(result.entities["datetime_time"])) | |
self.assertEqual("ampm", result.entities["datetime_time"][0]["comment"]) | |
self.assertEqual("T04", result.entities["datetime_time"][0]["time"]) | |
self.assertEqual(1, len(result.entities["$instance"]["datetime_time"])) | |
async def test_trace_activity(self): | |
# Arrange | |
utterance: str = "My name is Emad" | |
response_path: str = "TraceActivity.json" | |
# add async support to magic mock. | |
async def async_magic(): | |
pass | |
MagicMock.__await__ = lambda x: async_magic().__await__() | |
# Act | |
with mock.patch.object(TurnContext, "send_activity") as mock_send_activity: | |
await LuisRecognizerTest._get_recognizer_result(utterance, response_path) | |
trace_activity: Activity = mock_send_activity.call_args[0][0] | |
# Assert | |
self.assertIsNotNone(trace_activity) | |
self.assertEqual(LuisRecognizer.luis_trace_type, trace_activity.value_type) | |
self.assertEqual(LuisRecognizer.luis_trace_label, trace_activity.label) | |
luis_trace_info = trace_activity.value | |
self.assertIsNotNone(luis_trace_info) | |
self.assertIsNotNone(luis_trace_info["recognizerResult"]) | |
self.assertIsNotNone(luis_trace_info["luisResult"]) | |
self.assertIsNotNone(luis_trace_info["luisOptions"]) | |
self.assertIsNotNone(luis_trace_info["luisModel"]) | |
recognizer_result: RecognizerResult = luis_trace_info["recognizerResult"] | |
self.assertEqual(utterance, recognizer_result["text"]) | |
self.assertIsNotNone(recognizer_result["intents"]["SpecifyName"]) | |
self.assertEqual(utterance, luis_trace_info["luisResult"]["query"]) | |
self.assertEqual( | |
LuisRecognizerTest._luisAppId, luis_trace_info["luisModel"]["ModelID"] | |
) | |
self.assertIsNone(luis_trace_info["luisOptions"]["Staging"]) | |
def test_top_intent_returns_top_intent(self): | |
greeting_intent: str = LuisRecognizer.top_intent(self._mocked_results) | |
self.assertEqual(greeting_intent, "Greeting") | |
def test_top_intent_returns_default_intent_if_min_score_is_higher(self): | |
default_intent: str = LuisRecognizer.top_intent( | |
self._mocked_results, min_score=0.5 | |
) | |
self.assertEqual(default_intent, "None") | |
def test_top_intent_returns_default_intent_if_provided(self): | |
default_intent: str = LuisRecognizer.top_intent( | |
self._mocked_results, "Test2", 0.5 | |
) | |
self.assertEqual(default_intent, "Test2") | |
def test_top_intent_throws_type_error_if_results_is_none(self): | |
none_results: RecognizerResult = None | |
with self.assertRaises(TypeError): | |
LuisRecognizer.top_intent(none_results) | |
def test_top_intent_returns_top_intent_if_score_equals_min_score(self): | |
default_intent: str = LuisRecognizer.top_intent( | |
self._mocked_results, min_score=0.4 | |
) | |
self.assertEqual(default_intent, "Greeting") | |
def test_telemetry_construction(self): | |
# Arrange | |
# Note this is NOT a real LUIS application ID nor a real LUIS subscription-key | |
# theses are GUIDs edited to look right to the parsing and validation code. | |
endpoint = ( | |
"https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/" | |
"b31aeaf3-3511-495b-a07f-571fc873214b?verbose=true&timezoneOffset=-360" | |
"&subscription-key=048ec46dc58e495482b0c447cfdbd291&q=" | |
) | |
# Act | |
recognizer = LuisRecognizer(endpoint) | |
# Assert | |
app = recognizer._application | |
self.assertEqual("b31aeaf3-3511-495b-a07f-571fc873214b", app.application_id) | |
self.assertEqual("048ec46dc58e495482b0c447cfdbd291", app.endpoint_key) | |
self.assertEqual("https://westus.api.cognitive.microsoft.com", app.endpoint) | |
async def test_telemetry_override_on_log_async(self): | |
# Arrange | |
utterance: str = "please book from May 5 to June 6" | |
response_json: Dict[str, object] = self._empty_luis_response | |
telemetry_client = mock.create_autospec(BotTelemetryClient) | |
options = LuisPredictionOptions( | |
telemetry_client=telemetry_client, log_personal_information=False | |
) | |
telemetry_properties: Dict[str, str] = {"test": "testvalue", "foo": "foovalue"} | |
# Act | |
await LuisRecognizerTest._get_recognizer_result( | |
utterance, | |
response_json, | |
bot_adapter=NullAdapter(), | |
options=options, | |
telemetry_properties=telemetry_properties, | |
) | |
# Assert | |
self.assertEqual(1, telemetry_client.track_event.call_count) | |
args = telemetry_client.track_event.call_args[0] | |
self.assertEqual("LuisResult", args[0]) | |
self.assertTrue("applicationId" in args[1]) | |
self.assertTrue("intent" in args[1]) | |
self.assertTrue("intentScore" in args[1]) | |
self.assertTrue("fromId" in args[1]) | |
self.assertTrue("entities" in args[1]) | |
async def test_telemetry_pii_logged_async(self): | |
# Arrange | |
utterance: str = "please book from May 5 to June 6" | |
response_json: Dict[str, object] = self._empty_luis_response | |
telemetry_client = mock.create_autospec(BotTelemetryClient) | |
options = LuisPredictionOptions( | |
telemetry_client=telemetry_client, log_personal_information=True | |
) | |
# Act | |
await LuisRecognizerTest._get_recognizer_result( | |
utterance, | |
response_json, | |
bot_adapter=NullAdapter(), | |
options=options, | |
telemetry_properties=None, | |
) | |
# Assert | |
self.assertEqual(1, telemetry_client.track_event.call_count) | |
args = telemetry_client.track_event.call_args[0] | |
self.assertEqual("LuisResult", args[0]) | |
self.assertEqual(8, len(args[1])) | |
self.assertTrue("applicationId" in args[1]) | |
self.assertTrue("intent" in args[1]) | |
self.assertTrue("intentScore" in args[1]) | |
self.assertTrue("intent2" in args[1]) | |
self.assertTrue("intentScore2" in args[1]) | |
self.assertTrue("fromId" in args[1]) | |
self.assertTrue("entities" in args[1]) | |
self.assertTrue("question" in args[1]) | |
async def test_telemetry_no_pii_logged_async(self): | |
# Arrange | |
utterance: str = "please book from May 5 to June 6" | |
response_json: Dict[str, object] = self._empty_luis_response | |
telemetry_client = mock.create_autospec(BotTelemetryClient) | |
options = LuisPredictionOptions( | |
telemetry_client=telemetry_client, log_personal_information=False | |
) | |
# Act | |
await LuisRecognizerTest._get_recognizer_result( | |
utterance, | |
response_json, | |
bot_adapter=NullAdapter(), | |
options=options, | |
telemetry_properties=None, | |
) | |
# Assert | |
self.assertEqual(1, telemetry_client.track_event.call_count) | |
args = telemetry_client.track_event.call_args[0] | |
self.assertEqual("LuisResult", args[0]) | |
self.assertEqual(7, len(args[1])) | |
self.assertTrue("applicationId" in args[1]) | |
self.assertTrue("intent" in args[1]) | |
self.assertTrue("intentScore" in args[1]) | |
self.assertTrue("intent2" in args[1]) | |
self.assertTrue("intentScore2" in args[1]) | |
self.assertTrue("fromId" in args[1]) | |
self.assertTrue("entities" in args[1]) | |
self.assertFalse("question" in args[1]) | |
async def test_telemetry_override_on_derive_async(self): | |
# Arrange | |
utterance: str = "please book from May 5 to June 6" | |
response_json: Dict[str, object] = self._empty_luis_response | |
telemetry_client = mock.create_autospec(BotTelemetryClient) | |
options = LuisPredictionOptions( | |
telemetry_client=telemetry_client, log_personal_information=False | |
) | |
telemetry_properties: Dict[str, str] = {"test": "testvalue", "foo": "foovalue"} | |
# Act | |
await LuisRecognizerTest._get_recognizer_result( | |
utterance, | |
response_json, | |
bot_adapter=NullAdapter(), | |
options=options, | |
telemetry_properties=telemetry_properties, | |
recognizer_class=TelemetryOverrideRecognizer, | |
) | |
# Assert | |
self.assertEqual(2, telemetry_client.track_event.call_count) | |
call0_args = telemetry_client.track_event.call_args_list[0][0] | |
self.assertEqual("LuisResult", call0_args[0]) | |
self.assertTrue("MyImportantProperty" in call0_args[1]) | |
self.assertTrue(call0_args[1]["MyImportantProperty"] == "myImportantValue") | |
self.assertTrue("test" in call0_args[1]) | |
self.assertTrue(call0_args[1]["test"] == "testvalue") | |
self.assertTrue("foo" in call0_args[1]) | |
self.assertTrue(call0_args[1]["foo"] == "foovalue") | |
call1_args = telemetry_client.track_event.call_args_list[1][0] | |
self.assertEqual("MySecondEvent", call1_args[0]) | |
self.assertTrue("MyImportantProperty2" in call1_args[1]) | |
self.assertTrue(call1_args[1]["MyImportantProperty2"] == "myImportantValue2") | |
async def test_telemetry_override_fill_async(self): | |
# Arrange | |
utterance: str = "please book from May 5 to June 6" | |
response_json: Dict[str, object] = self._empty_luis_response | |
telemetry_client = mock.create_autospec(BotTelemetryClient) | |
options = LuisPredictionOptions( | |
telemetry_client=telemetry_client, log_personal_information=False | |
) | |
additional_properties: Dict[str, str] = {"test": "testvalue", "foo": "foovalue"} | |
additional_metrics: Dict[str, str] = {"moo": 3.14159, "boo": 2.11} | |
# Act | |
await LuisRecognizerTest._get_recognizer_result( | |
utterance, | |
response_json, | |
bot_adapter=NullAdapter(), | |
options=options, | |
telemetry_properties=additional_properties, | |
telemetry_metrics=additional_metrics, | |
recognizer_class=OverrideFillRecognizer, | |
) | |
# Assert | |
self.assertEqual(2, telemetry_client.track_event.call_count) | |
call0_args = telemetry_client.track_event.call_args_list[0][0] | |
self.assertEqual("LuisResult", call0_args[0]) | |
self.assertTrue("MyImportantProperty" in call0_args[1]) | |
self.assertTrue(call0_args[1]["MyImportantProperty"] == "myImportantValue") | |
self.assertTrue("test" in call0_args[1]) | |
self.assertTrue(call0_args[1]["test"] == "testvalue") | |
self.assertTrue("foo" in call0_args[1]) | |
self.assertTrue(call0_args[1]["foo"] == "foovalue") | |
self.assertTrue("moo" in call0_args[2]) | |
self.assertTrue(call0_args[2]["moo"] == 3.14159) | |
self.assertTrue("boo" in call0_args[2]) | |
self.assertTrue(call0_args[2]["boo"] == 2.11) | |
call1_args = telemetry_client.track_event.call_args_list[1][0] | |
self.assertEqual("MySecondEvent", call1_args[0]) | |
self.assertTrue("MyImportantProperty2" in call1_args[1]) | |
self.assertTrue(call1_args[1]["MyImportantProperty2"] == "myImportantValue2") | |
async def test_telemetry_no_override_async(self): | |
# Arrange | |
utterance: str = "please book from May 5 to June 6" | |
response_json: Dict[str, object] = self._empty_luis_response | |
telemetry_client = mock.create_autospec(BotTelemetryClient) | |
options = LuisPredictionOptions( | |
telemetry_client=telemetry_client, log_personal_information=False | |
) | |
# Act | |
await LuisRecognizerTest._get_recognizer_result( | |
utterance, response_json, bot_adapter=NullAdapter(), options=options | |
) | |
# Assert | |
self.assertEqual(1, telemetry_client.track_event.call_count) | |
call0_args = telemetry_client.track_event.call_args_list[0][0] | |
self.assertEqual("LuisResult", call0_args[0]) | |
self.assertTrue("intent" in call0_args[1]) | |
self.assertTrue("intentScore" in call0_args[1]) | |
self.assertTrue("fromId" in call0_args[1]) | |
self.assertTrue("entities" in call0_args[1]) | |
def test_pass_luis_prediction_options_to_recognizer(self): | |
# Arrange | |
my_app = LuisApplication( | |
LuisRecognizerTest._luisAppId, | |
LuisRecognizerTest._subscriptionKey, | |
endpoint=None, | |
) | |
luis_prediction_options = LuisPredictionOptions( | |
log_personal_information=True, | |
include_all_intents=True, | |
include_instance_data=True, | |
) | |
# Assert | |
recognizer = LuisRecognizer(my_app) | |
merged_options = recognizer._merge_options(luis_prediction_options) | |
self.assertTrue(merged_options.log_personal_information) | |
self.assertTrue(merged_options.include_all_intents) | |
self.assertTrue(merged_options.include_instance_data) | |
self.assertFalse(recognizer._options.log_personal_information) | |
self.assertFalse(recognizer._options.include_all_intents) | |
self.assertFalse(recognizer._options.include_instance_data) | |
def test_dont_pass_luis_prediction_options_to_recognizer(self): | |
# Arrange | |
my_app = LuisApplication( | |
LuisRecognizerTest._luisAppId, | |
LuisRecognizerTest._subscriptionKey, | |
endpoint=None, | |
) | |
# Assert | |
recognizer = LuisRecognizer(my_app) | |
self.assertFalse(recognizer._options.log_personal_information) | |
self.assertFalse(recognizer._options.include_all_intents) | |
self.assertFalse(recognizer._options.include_instance_data) | |
async def test_composite1(self): | |
await self._test_json("Composite1.json") | |
async def test_composite2(self): | |
await self._test_json("Composite2.json") | |
async def test_composite3(self): | |
await self._test_json("Composite3.json") | |
async def test_prebuilt_domains(self): | |
await self._test_json("Prebuilt.json") | |
async def test_patterns(self): | |
await self._test_json("Patterns.json") | |
def assert_score(self, score: float) -> None: | |
self.assertTrue(score >= 0) | |
self.assertTrue(score <= 1) | |
async def _test_json(self, response_file: str) -> None: | |
# Arrange | |
expected_json = LuisRecognizerTest._get_json_for_file(response_file) | |
response_json = expected_json["luisResult"] | |
utterance = expected_json.get("text") | |
if utterance is None: | |
utterance = expected_json.get("Text") | |
options = LuisPredictionOptions(include_all_intents=True) | |
# Act | |
_, result = await LuisRecognizerTest._get_recognizer_result( | |
utterance, response_json, options=options, include_api_results=True | |
) | |
# Assert | |
actual_result_json = LuisUtil.recognizer_result_as_dict(result) | |
trimmed_expected = LuisRecognizerTest._remove_none_property(expected_json) | |
trimmed_actual = LuisRecognizerTest._remove_none_property(actual_result_json) | |
self.assertEqual(trimmed_expected, trimmed_actual) | |
def _remove_none_property(dictionary: Dict[str, object]) -> Dict[str, object]: | |
for key, value in list(dictionary.items()): | |
if value is None: | |
del dictionary[key] | |
elif isinstance(value, dict): | |
LuisRecognizerTest._remove_none_property(value) | |
return dictionary | |
async def _get_recognizer_result( | |
cls, | |
utterance: str, | |
response_json: Union[str, Dict[str, object]], | |
bot_adapter: BotAdapter = TestAdapter(), | |
options: LuisPredictionOptions = None, | |
include_api_results: bool = False, | |
telemetry_properties: Dict[str, str] = None, | |
telemetry_metrics: Dict[str, float] = None, | |
recognizer_class: type = LuisRecognizer, | |
) -> Tuple[LuisRecognizer, RecognizerResult]: | |
if isinstance(response_json, str): | |
response_json = LuisRecognizerTest._get_json_for_file( | |
response_file=response_json | |
) | |
recognizer = LuisRecognizerTest._get_luis_recognizer( | |
recognizer_class, include_api_results=include_api_results, options=options | |
) | |
context = LuisRecognizerTest._get_context(utterance, bot_adapter) | |
response = Mock(spec=Response) | |
response.status_code = 200 | |
response.headers = {} | |
response.reason = "" | |
with mock.patch.object(Session, "send", return_value=response): | |
with mock.patch.object( | |
Deserializer, "_unpack_content", return_value=response_json | |
): | |
result = await recognizer.recognize( | |
context, telemetry_properties, telemetry_metrics | |
) | |
return recognizer, result | |
def _get_json_for_file(cls, response_file: str) -> Dict[str, object]: | |
curr_dir = path.dirname(path.abspath(__file__)) | |
response_path = path.join(curr_dir, "test_data", response_file) | |
with open(response_path, "r", encoding="utf-8-sig") as file: | |
response_str = file.read() | |
response_json = json.loads(response_str) | |
return response_json | |
def _get_luis_recognizer( | |
cls, | |
recognizer_class: type, | |
options: LuisPredictionOptions = None, | |
include_api_results: bool = False, | |
) -> LuisRecognizer: | |
luis_app = LuisApplication(cls._luisAppId, cls._subscriptionKey, cls._endpoint) | |
return recognizer_class( | |
luis_app, | |
prediction_options=options, | |
include_api_results=include_api_results, | |
) | |
def _get_context(utterance: str, bot_adapter: BotAdapter) -> TurnContext: | |
activity = Activity( | |
type=ActivityTypes.message, | |
text=utterance, | |
conversation=ConversationAccount(), | |
recipient=ChannelAccount(), | |
from_property=ChannelAccount(), | |
) | |
return TurnContext(bot_adapter, activity) | |