We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[PORT] Fix LuisRecognizer to not return intents if there is no utterance
> Port this change from botbuilder-dotnet/master branch:
https://github.com/microsoft/botbuilder-dotnet/pull/4129

Fixes #4117 

## Description
Luis Recognizer returns an intent of string.empty with a perfect score of 1.0.  This messes up logic downstream in RecognizerSet because it thinks it has a perfect intent.

## Specific Changes
* Changed LuisRecognizer to return empy RecognizerResult (no intents or entities) when there is no utterance.

## Testing
Changed RegexRecognizer to behave just like LuisRecognizer so that ValueRecognizer tests are valid check.


# Changed projects
* Microsoft.Bot.Builder.AI.LUIS
* Microsoft.Bot.Builder.Dialogs.Adaptive
* Microsoft.Bot.Builder.AI.LUIS.Tests





--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `libraries/botbuilder-ai/botbuilder/ai/luis/luis_recognizer.py`
Content:
```
1 # Copyright (c) Microsoft Corporation. All rights reserved.
2 # Licensed under the MIT License.
3 
4 import json
5 from typing import Dict, List, Tuple, Union
6 from botbuilder.core import (
7     BotAssert,
8     IntentScore,
9     Recognizer,
10     RecognizerResult,
11     TurnContext,
12 )
13 from botbuilder.schema import ActivityTypes
14 from . import LuisApplication, LuisPredictionOptions, LuisTelemetryConstants
15 from .luis_recognizer_v3 import LuisRecognizerV3
16 from .luis_recognizer_v2 import LuisRecognizerV2
17 from .luis_recognizer_options_v2 import LuisRecognizerOptionsV2
18 from .luis_recognizer_options_v3 import LuisRecognizerOptionsV3
19 
20 
21 class LuisRecognizer(Recognizer):
22     """
23     A LUIS based implementation of <see cref="IRecognizer"/>.
24     """
25 
26     # The value type for a LUIS trace activity.
27     luis_trace_type: str = "https://www.luis.ai/schemas/trace"
28 
29     # The context label for a LUIS trace activity.
30     luis_trace_label: str = "Luis Trace"
31 
32     def __init__(
33         self,
34         application: Union[LuisApplication, str],
35         prediction_options: Union[
36             LuisRecognizerOptionsV2, LuisRecognizerOptionsV3, LuisPredictionOptions
37         ] = None,
38         include_api_results: bool = False,
39     ):
40         """Initializes a new instance of the :class:`LuisRecognizer` class.
41 
42         :param application: The LUIS application to use to recognize text.
43         :type application: :class:`LuisApplication`
44         :param prediction_options: The LUIS prediction options to use, defaults to None.
45         :type prediction_options: :class:`LuisPredictionOptions`, optional
46         :param include_api_results: True to include raw LUIS API response, defaults to False.
47         :type include_api_results: bool, optional
48         :raises TypeError:
49         """
50 
51         if isinstance(application, LuisApplication):
52             self._application = application
53         elif isinstance(application, str):
54             self._application = LuisApplication.from_application_endpoint(application)
55         else:
56             raise TypeError(
57                 "LuisRecognizer.__init__(): application is not an instance of LuisApplication or str."
58             )
59 
60         self._options = prediction_options or LuisPredictionOptions()
61         self._include_api_results = include_api_results or (
62             prediction_options.include_api_results
63             if isinstance(
64                 prediction_options, (LuisRecognizerOptionsV3, LuisRecognizerOptionsV2)
65             )
66             else False
67         )
68 
69         self.telemetry_client = self._options.telemetry_client
70         self.log_personal_information = self._options.log_personal_information
71 
72     @staticmethod
73     def top_intent(
74         results: RecognizerResult, default_intent: str = "None", min_score: float = 0.0
75     ) -> str:
76         """Returns the name of the top scoring intent from a set of LUIS results.
77 
78         :param results: Result set to be searched.
79         :type results: :class:`botbuilder.core.RecognizerResult`
80         :param default_intent: Intent name to return should a top intent be found, defaults to None.
81         :type default_intent: str, optional
82         :param min_score: Minimum score needed for an intent to be considered as a top intent. If all intents in
83         the set are below this threshold then the `defaultIntent` will be returned, defaults to 0.0.
84         :type min_score: float, optional
85         :raises TypeError:
86         :return: The top scoring intent name.
87         :rtype: str
88         """
89 
90         if results is None:
91             raise TypeError("LuisRecognizer.top_intent(): results cannot be None.")
92 
93         top_intent: str = None
94         top_score: float = -1.0
95         if results.intents:
96             for intent_name, intent_score in results.intents.items():
97                 score = intent_score.score
98                 if score > top_score and score >= min_score:
99                     top_intent = intent_name
100                     top_score = score
101 
102         return top_intent or default_intent
103 
104     async def recognize(  # pylint: disable=arguments-differ
105         self,
106         turn_context: TurnContext,
107         telemetry_properties: Dict[str, str] = None,
108         telemetry_metrics: Dict[str, float] = None,
109         luis_prediction_options: LuisPredictionOptions = None,
110     ) -> RecognizerResult:
111         """Return results of the analysis (Suggested actions and intents).
112 
113         :param turn_context: Context object containing information for a single turn of conversation with a user.
114         :type turn_context: :class:`botbuilder.core.TurnContext`
115         :param telemetry_properties: Additional properties to be logged to telemetry with the LuisResult event, defaults
116          to None.
117         :type telemetry_properties: :class:`typing.Dict[str, str]`, optional
118         :param telemetry_metrics: Additional metrics to be logged to telemetry with the LuisResult event, defaults to
119          None.
120         :type telemetry_metrics: :class:`typing.Dict[str, float]`, optional
121         :return: The LUIS results of the analysis of the current message text in the current turn's context activity.
122         :rtype: :class:`botbuilder.core.RecognizerResult`
123         """
124 
125         return await self._recognize_internal(
126             turn_context,
127             telemetry_properties,
128             telemetry_metrics,
129             luis_prediction_options,
130         )
131 
132     def on_recognizer_result(
133         self,
134         recognizer_result: RecognizerResult,
135         turn_context: TurnContext,
136         telemetry_properties: Dict[str, str] = None,
137         telemetry_metrics: Dict[str, float] = None,
138     ):
139         """Invoked prior to a LuisResult being logged.
140 
141         :param recognizer_result: The Luis Results for the call.
142         :type recognizer_result: :class:`botbuilder.core.RecognizerResult`
143         :param turn_context: Context object containing information for a single turn of conversation with a user.
144         :type turn_context: :class:`botbuilder.core.TurnContext`
145         :param telemetry_properties: Additional properties to be logged to telemetry with the LuisResult event, defaults
146          to None.
147         :type telemetry_properties: :class:`typing.Dict[str, str]`, optional
148         :param telemetry_metrics: Additional metrics to be logged to telemetry with the LuisResult event, defaults
149          to None.
150         :type telemetry_metrics: :class:`typing.Dict[str, float]`, optional
151         """
152 
153         properties = self.fill_luis_event_properties(
154             recognizer_result, turn_context, telemetry_properties
155         )
156 
157         # Track the event
158         self.telemetry_client.track_event(
159             LuisTelemetryConstants.luis_result, properties, telemetry_metrics
160         )
161 
162     @staticmethod
163     def _get_top_k_intent_score(
164         intent_names: List[str],
165         intents: Dict[str, IntentScore],
166         index: int,  # pylint: disable=unused-argument
167     ) -> Tuple[str, str]:
168         intent_name = ""
169         intent_score = "0.00"
170         if intent_names:
171             intent_name = intent_names[0]
172             if intents[intent_name] is not None:
173                 intent_score = "{:.2f}".format(intents[intent_name].score)
174 
175         return intent_name, intent_score
176 
177     def fill_luis_event_properties(
178         self,
179         recognizer_result: RecognizerResult,
180         turn_context: TurnContext,
181         telemetry_properties: Dict[str, str] = None,
182     ) -> Dict[str, str]:
183         """Fills the event properties for LuisResult event for telemetry.
184         These properties are logged when the recognizer is called.
185 
186         :param recognizer_result: Last activity sent from user.
187         :type recognizer_result: :class:`botbuilder.core.RecognizerResult`
188         :param turn_context: Context object containing information for a single turn of conversation with a user.
189         :type turn_context: :class:`botbuilder.core.TurnContext`
190         :param telemetry_properties: Additional properties to be logged to telemetry with the LuisResult event,
191          defaults to None
192         :param telemetry_properties: :class:`typing.Dict[str, str]`, optional
193         :return: A dictionary that is sent as "Properties" to :func:`botbuilder.core.BotTelemetryClient.track_event`
194         method for the BotMessageSend event.
195         :rtype: `typing.Dict[str, str]`
196         """
197 
198         intents = recognizer_result.intents
199         top_two_intents = (
200             sorted(intents.keys(), key=lambda k: intents[k].score, reverse=True)[:2]
201             if intents
202             else []
203         )
204 
205         intent_name, intent_score = LuisRecognizer._get_top_k_intent_score(
206             top_two_intents, intents, index=0
207         )
208         intent2_name, intent2_score = LuisRecognizer._get_top_k_intent_score(
209             top_two_intents, intents, index=1
210         )
211 
212         # Add the intent score and conversation id properties
213         properties: Dict[str, str] = {
214             LuisTelemetryConstants.application_id_property: self._application.application_id,
215             LuisTelemetryConstants.intent_property: intent_name,
216             LuisTelemetryConstants.intent_score_property: intent_score,
217             LuisTelemetryConstants.intent2_property: intent2_name,
218             LuisTelemetryConstants.intent_score2_property: intent2_score,
219             LuisTelemetryConstants.from_id_property: turn_context.activity.from_property.id,
220         }
221 
222         sentiment = recognizer_result.properties.get("sentiment")
223         if sentiment is not None and isinstance(sentiment, Dict):
224             label = sentiment.get("label")
225             if label is not None:
226                 properties[LuisTelemetryConstants.sentiment_label_property] = str(label)
227 
228             score = sentiment.get("score")
229             if score is not None:
230                 properties[LuisTelemetryConstants.sentiment_score_property] = str(score)
231 
232         entities = None
233         if recognizer_result.entities is not None:
234             entities = json.dumps(recognizer_result.entities)
235         properties[LuisTelemetryConstants.entities_property] = entities
236 
237         # Use the LogPersonalInformation flag to toggle logging PII data, text is a common example
238         if self.log_personal_information and turn_context.activity.text:
239             properties[
240                 LuisTelemetryConstants.question_property
241             ] = turn_context.activity.text
242 
243         # Additional Properties can override "stock" properties.
244         if telemetry_properties is not None:
245             for key in telemetry_properties:
246                 properties[key] = telemetry_properties[key]
247 
248         return properties
249 
250     async def _recognize_internal(
251         self,
252         turn_context: TurnContext,
253         telemetry_properties: Dict[str, str],
254         telemetry_metrics: Dict[str, float],
255         luis_prediction_options: Union[
256             LuisPredictionOptions, LuisRecognizerOptionsV2, LuisRecognizerOptionsV3
257         ] = None,
258     ) -> RecognizerResult:
259 
260         BotAssert.context_not_none(turn_context)
261 
262         if turn_context.activity.type != ActivityTypes.message:
263             return None
264 
265         utterance: str = turn_context.activity.text if turn_context.activity is not None else None
266         recognizer_result: RecognizerResult = None
267 
268         if luis_prediction_options:
269             options = luis_prediction_options
270         else:
271             options = self._options
272 
273         if not utterance or utterance.isspace():
274             recognizer_result = RecognizerResult(
275                 text=utterance, intents={"": IntentScore(score=1.0)}, entities={}
276             )
277         else:
278 
279             luis_recognizer = self._build_recognizer(options)
280             recognizer_result = await luis_recognizer.recognizer_internal(turn_context)
281 
282         # Log telemetry
283         self.on_recognizer_result(
284             recognizer_result, turn_context, telemetry_properties, telemetry_metrics
285         )
286 
287         return recognizer_result
288 
289     def _merge_options(
290         self,
291         user_defined_options: Union[
292             LuisRecognizerOptionsV3, LuisRecognizerOptionsV2, LuisPredictionOptions
293         ],
294     ) -> LuisPredictionOptions:
295         merged_options = LuisPredictionOptions()
296         merged_options.__dict__.update(user_defined_options.__dict__)
297         return merged_options
298 
299     def _build_recognizer(
300         self,
301         luis_prediction_options: Union[
302             LuisRecognizerOptionsV3, LuisRecognizerOptionsV2, LuisPredictionOptions
303         ],
304     ):
305         if isinstance(luis_prediction_options, LuisRecognizerOptionsV3):
306             return LuisRecognizerV3(self._application, luis_prediction_options)
307         if isinstance(luis_prediction_options, LuisRecognizerOptionsV2):
308             return LuisRecognizerV3(self._application, luis_prediction_options)
309 
310         recognizer_options = LuisRecognizerOptionsV2(
311             luis_prediction_options.bing_spell_check_subscription_key,
312             luis_prediction_options.include_all_intents,
313             luis_prediction_options.include_instance_data,
314             luis_prediction_options.log,
315             luis_prediction_options.spell_check,
316             luis_prediction_options.staging,
317             luis_prediction_options.timeout,
318             luis_prediction_options.timezone_offset,
319             self._include_api_results,
320             luis_prediction_options.telemetry_client,
321             luis_prediction_options.log_personal_information,
322         )
323         return LuisRecognizerV2(self._application, recognizer_options)
324
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/libraries/botbuilder-ai/botbuilder/ai/luis/luis_recognizer.py b/libraries/botbuilder-ai/botbuilder/ai/luis/luis_recognizer.py
--- a/libraries/botbuilder-ai/botbuilder/ai/luis/luis_recognizer.py
+++ b/libraries/botbuilder-ai/botbuilder/ai/luis/luis_recognizer.py
@@ -272,7 +272,7 @@
 
         if not utterance or utterance.isspace():
             recognizer_result = RecognizerResult(
-                text=utterance, intents={"": IntentScore(score=1.0)}, entities={}
+                text=utterance
             )
         else:

{"golden_diff": "diff --git a/libraries/botbuilder-ai/botbuilder/ai/luis/luis_recognizer.py b/libraries/botbuilder-ai/botbuilder/ai/luis/luis_recognizer.py\n--- a/libraries/botbuilder-ai/botbuilder/ai/luis/luis_recognizer.py\n+++ b/libraries/botbuilder-ai/botbuilder/ai/luis/luis_recognizer.py\n@@ -272,7 +272,7 @@\n \n         if not utterance or utterance.isspace():\n             recognizer_result = RecognizerResult(\n-                text=utterance, intents={\"\": IntentScore(score=1.0)}, entities={}\n+                text=utterance\n             )\n         else:\n", "issue": "[PORT] Fix LuisRecognizer to not return intents if there is no utterance\n> Port this change from botbuilder-dotnet/master branch:\nhttps://github.com/microsoft/botbuilder-dotnet/pull/4129\n\nFixes #4117 \r\n\r\n## Description\r\nLuis Recognizer returns an intent of string.empty with a perfect score of 1.0.  This messes up logic downstream in RecognizerSet because it thinks it has a perfect intent.\r\n\r\n## Specific Changes\r\n* Changed LuisRecognizer to return empy RecognizerResult (no intents or entities) when there is no utterance.\r\n\r\n## Testing\r\nChanged RegexRecognizer to behave just like LuisRecognizer so that ValueRecognizer tests are valid check.\n\n\r\n# Changed projects\r\n* Microsoft.Bot.Builder.AI.LUIS\r\n* Microsoft.Bot.Builder.Dialogs.Adaptive\r\n* Microsoft.Bot.Builder.AI.LUIS.Tests\r\n\r\n\r\n\n\n", "before_files": [{"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License.\n\nimport json\nfrom typing import Dict, List, Tuple, Union\nfrom botbuilder.core import (\n    BotAssert,\n    IntentScore,\n    Recognizer,\n    RecognizerResult,\n    TurnContext,\n)\nfrom botbuilder.schema import ActivityTypes\nfrom . import LuisApplication, LuisPredictionOptions, LuisTelemetryConstants\nfrom .luis_recognizer_v3 import LuisRecognizerV3\nfrom .luis_recognizer_v2 import LuisRecognizerV2\nfrom .luis_recognizer_options_v2 import LuisRecognizerOptionsV2\nfrom .luis_recognizer_options_v3 import LuisRecognizerOptionsV3\n\n\nclass LuisRecognizer(Recognizer):\n    \"\"\"\n    A LUIS based implementation of <see cref=\"IRecognizer\"/>.\n    \"\"\"\n\n    # The value type for a LUIS trace activity.\n    luis_trace_type: str = \"https://www.luis.ai/schemas/trace\"\n\n    # The context label for a LUIS trace activity.\n    luis_trace_label: str = \"Luis Trace\"\n\n    def __init__(\n        self,\n        application: Union[LuisApplication, str],\n        prediction_options: Union[\n            LuisRecognizerOptionsV2, LuisRecognizerOptionsV3, LuisPredictionOptions\n        ] = None,\n        include_api_results: bool = False,\n    ):\n        \"\"\"Initializes a new instance of the :class:`LuisRecognizer` class.\n\n        :param application: The LUIS application to use to recognize text.\n        :type application: :class:`LuisApplication`\n        :param prediction_options: The LUIS prediction options to use, defaults to None.\n        :type prediction_options: :class:`LuisPredictionOptions`, optional\n        :param include_api_results: True to include raw LUIS API response, defaults to False.\n        :type include_api_results: bool, optional\n        :raises TypeError:\n        \"\"\"\n\n        if isinstance(application, LuisApplication):\n            self._application = application\n        elif isinstance(application, str):\n            self._application = LuisApplication.from_application_endpoint(application)\n        else:\n            raise TypeError(\n                \"LuisRecognizer.__init__(): application is not an instance of LuisApplication or str.\"\n            )\n\n        self._options = prediction_options or LuisPredictionOptions()\n        self._include_api_results = include_api_results or (\n            prediction_options.include_api_results\n            if isinstance(\n                prediction_options, (LuisRecognizerOptionsV3, LuisRecognizerOptionsV2)\n            )\n            else False\n        )\n\n        self.telemetry_client = self._options.telemetry_client\n        self.log_personal_information = self._options.log_personal_information\n\n    @staticmethod\n    def top_intent(\n        results: RecognizerResult, default_intent: str = \"None\", min_score: float = 0.0\n    ) -> str:\n        \"\"\"Returns the name of the top scoring intent from a set of LUIS results.\n\n        :param results: Result set to be searched.\n        :type results: :class:`botbuilder.core.RecognizerResult`\n        :param default_intent: Intent name to return should a top intent be found, defaults to None.\n        :type default_intent: str, optional\n        :param min_score: Minimum score needed for an intent to be considered as a top intent. If all intents in\n        the set are below this threshold then the `defaultIntent` will be returned, defaults to 0.0.\n        :type min_score: float, optional\n        :raises TypeError:\n        :return: The top scoring intent name.\n        :rtype: str\n        \"\"\"\n\n        if results is None:\n            raise TypeError(\"LuisRecognizer.top_intent(): results cannot be None.\")\n\n        top_intent: str = None\n        top_score: float = -1.0\n        if results.intents:\n            for intent_name, intent_score in results.intents.items():\n                score = intent_score.score\n                if score > top_score and score >= min_score:\n                    top_intent = intent_name\n                    top_score = score\n\n        return top_intent or default_intent\n\n    async def recognize(  # pylint: disable=arguments-differ\n        self,\n        turn_context: TurnContext,\n        telemetry_properties: Dict[str, str] = None,\n        telemetry_metrics: Dict[str, float] = None,\n        luis_prediction_options: LuisPredictionOptions = None,\n    ) -> RecognizerResult:\n        \"\"\"Return results of the analysis (Suggested actions and intents).\n\n        :param turn_context: Context object containing information for a single turn of conversation with a user.\n        :type turn_context: :class:`botbuilder.core.TurnContext`\n        :param telemetry_properties: Additional properties to be logged to telemetry with the LuisResult event, defaults\n         to None.\n        :type telemetry_properties: :class:`typing.Dict[str, str]`, optional\n        :param telemetry_metrics: Additional metrics to be logged to telemetry with the LuisResult event, defaults to\n         None.\n        :type telemetry_metrics: :class:`typing.Dict[str, float]`, optional\n        :return: The LUIS results of the analysis of the current message text in the current turn's context activity.\n        :rtype: :class:`botbuilder.core.RecognizerResult`\n        \"\"\"\n\n        return await self._recognize_internal(\n            turn_context,\n            telemetry_properties,\n            telemetry_metrics,\n            luis_prediction_options,\n        )\n\n    def on_recognizer_result(\n        self,\n        recognizer_result: RecognizerResult,\n        turn_context: TurnContext,\n        telemetry_properties: Dict[str, str] = None,\n        telemetry_metrics: Dict[str, float] = None,\n    ):\n        \"\"\"Invoked prior to a LuisResult being logged.\n\n        :param recognizer_result: The Luis Results for the call.\n        :type recognizer_result: :class:`botbuilder.core.RecognizerResult`\n        :param turn_context: Context object containing information for a single turn of conversation with a user.\n        :type turn_context: :class:`botbuilder.core.TurnContext`\n        :param telemetry_properties: Additional properties to be logged to telemetry with the LuisResult event, defaults\n         to None.\n        :type telemetry_properties: :class:`typing.Dict[str, str]`, optional\n        :param telemetry_metrics: Additional metrics to be logged to telemetry with the LuisResult event, defaults\n         to None.\n        :type telemetry_metrics: :class:`typing.Dict[str, float]`, optional\n        \"\"\"\n\n        properties = self.fill_luis_event_properties(\n            recognizer_result, turn_context, telemetry_properties\n        )\n\n        # Track the event\n        self.telemetry_client.track_event(\n            LuisTelemetryConstants.luis_result, properties, telemetry_metrics\n        )\n\n    @staticmethod\n    def _get_top_k_intent_score(\n        intent_names: List[str],\n        intents: Dict[str, IntentScore],\n        index: int,  # pylint: disable=unused-argument\n    ) -> Tuple[str, str]:\n        intent_name = \"\"\n        intent_score = \"0.00\"\n        if intent_names:\n            intent_name = intent_names[0]\n            if intents[intent_name] is not None:\n                intent_score = \"{:.2f}\".format(intents[intent_name].score)\n\n        return intent_name, intent_score\n\n    def fill_luis_event_properties(\n        self,\n        recognizer_result: RecognizerResult,\n        turn_context: TurnContext,\n        telemetry_properties: Dict[str, str] = None,\n    ) -> Dict[str, str]:\n        \"\"\"Fills the event properties for LuisResult event for telemetry.\n        These properties are logged when the recognizer is called.\n\n        :param recognizer_result: Last activity sent from user.\n        :type recognizer_result: :class:`botbuilder.core.RecognizerResult`\n        :param turn_context: Context object containing information for a single turn of conversation with a user.\n        :type turn_context: :class:`botbuilder.core.TurnContext`\n        :param telemetry_properties: Additional properties to be logged to telemetry with the LuisResult event,\n         defaults to None\n        :param telemetry_properties: :class:`typing.Dict[str, str]`, optional\n        :return: A dictionary that is sent as \"Properties\" to :func:`botbuilder.core.BotTelemetryClient.track_event`\n        method for the BotMessageSend event.\n        :rtype: `typing.Dict[str, str]`\n        \"\"\"\n\n        intents = recognizer_result.intents\n        top_two_intents = (\n            sorted(intents.keys(), key=lambda k: intents[k].score, reverse=True)[:2]\n            if intents\n            else []\n        )\n\n        intent_name, intent_score = LuisRecognizer._get_top_k_intent_score(\n            top_two_intents, intents, index=0\n        )\n        intent2_name, intent2_score = LuisRecognizer._get_top_k_intent_score(\n            top_two_intents, intents, index=1\n        )\n\n        # Add the intent score and conversation id properties\n        properties: Dict[str, str] = {\n            LuisTelemetryConstants.application_id_property: self._application.application_id,\n            LuisTelemetryConstants.intent_property: intent_name,\n            LuisTelemetryConstants.intent_score_property: intent_score,\n            LuisTelemetryConstants.intent2_property: intent2_name,\n            LuisTelemetryConstants.intent_score2_property: intent2_score,\n            LuisTelemetryConstants.from_id_property: turn_context.activity.from_property.id,\n        }\n\n        sentiment = recognizer_result.properties.get(\"sentiment\")\n        if sentiment is not None and isinstance(sentiment, Dict):\n            label = sentiment.get(\"label\")\n            if label is not None:\n                properties[LuisTelemetryConstants.sentiment_label_property] = str(label)\n\n            score = sentiment.get(\"score\")\n            if score is not None:\n                properties[LuisTelemetryConstants.sentiment_score_property] = str(score)\n\n        entities = None\n        if recognizer_result.entities is not None:\n            entities = json.dumps(recognizer_result.entities)\n        properties[LuisTelemetryConstants.entities_property] = entities\n\n        # Use the LogPersonalInformation flag to toggle logging PII data, text is a common example\n        if self.log_personal_information and turn_context.activity.text:\n            properties[\n                LuisTelemetryConstants.question_property\n            ] = turn_context.activity.text\n\n        # Additional Properties can override \"stock\" properties.\n        if telemetry_properties is not None:\n            for key in telemetry_properties:\n                properties[key] = telemetry_properties[key]\n\n        return properties\n\n    async def _recognize_internal(\n        self,\n        turn_context: TurnContext,\n        telemetry_properties: Dict[str, str],\n        telemetry_metrics: Dict[str, float],\n        luis_prediction_options: Union[\n            LuisPredictionOptions, LuisRecognizerOptionsV2, LuisRecognizerOptionsV3\n        ] = None,\n    ) -> RecognizerResult:\n\n        BotAssert.context_not_none(turn_context)\n\n        if turn_context.activity.type != ActivityTypes.message:\n            return None\n\n        utterance: str = turn_context.activity.text if turn_context.activity is not None else None\n        recognizer_result: RecognizerResult = None\n\n        if luis_prediction_options:\n            options = luis_prediction_options\n        else:\n            options = self._options\n\n        if not utterance or utterance.isspace():\n            recognizer_result = RecognizerResult(\n                text=utterance, intents={\"\": IntentScore(score=1.0)}, entities={}\n            )\n        else:\n\n            luis_recognizer = self._build_recognizer(options)\n            recognizer_result = await luis_recognizer.recognizer_internal(turn_context)\n\n        # Log telemetry\n        self.on_recognizer_result(\n            recognizer_result, turn_context, telemetry_properties, telemetry_metrics\n        )\n\n        return recognizer_result\n\n    def _merge_options(\n        self,\n        user_defined_options: Union[\n            LuisRecognizerOptionsV3, LuisRecognizerOptionsV2, LuisPredictionOptions\n        ],\n    ) -> LuisPredictionOptions:\n        merged_options = LuisPredictionOptions()\n        merged_options.__dict__.update(user_defined_options.__dict__)\n        return merged_options\n\n    def _build_recognizer(\n        self,\n        luis_prediction_options: Union[\n            LuisRecognizerOptionsV3, LuisRecognizerOptionsV2, LuisPredictionOptions\n        ],\n    ):\n        if isinstance(luis_prediction_options, LuisRecognizerOptionsV3):\n            return LuisRecognizerV3(self._application, luis_prediction_options)\n        if isinstance(luis_prediction_options, LuisRecognizerOptionsV2):\n            return LuisRecognizerV3(self._application, luis_prediction_options)\n\n        recognizer_options = LuisRecognizerOptionsV2(\n            luis_prediction_options.bing_spell_check_subscription_key,\n            luis_prediction_options.include_all_intents,\n            luis_prediction_options.include_instance_data,\n            luis_prediction_options.log,\n            luis_prediction_options.spell_check,\n            luis_prediction_options.staging,\n            luis_prediction_options.timeout,\n            luis_prediction_options.timezone_offset,\n            self._include_api_results,\n            luis_prediction_options.telemetry_client,\n            luis_prediction_options.log_personal_information,\n        )\n        return LuisRecognizerV2(self._application, recognizer_options)\n", "path": "libraries/botbuilder-ai/botbuilder/ai/luis/luis_recognizer.py"}], "after_files": [{"content": "# Copyright (c) Microsoft Corporation. All rights reserved.\n# Licensed under the MIT License.\n\nimport json\nfrom typing import Dict, List, Tuple, Union\nfrom botbuilder.core import (\n    BotAssert,\n    IntentScore,\n    Recognizer,\n    RecognizerResult,\n    TurnContext,\n)\nfrom botbuilder.schema import ActivityTypes\nfrom . import LuisApplication, LuisPredictionOptions, LuisTelemetryConstants\nfrom .luis_recognizer_v3 import LuisRecognizerV3\nfrom .luis_recognizer_v2 import LuisRecognizerV2\nfrom .luis_recognizer_options_v2 import LuisRecognizerOptionsV2\nfrom .luis_recognizer_options_v3 import LuisRecognizerOptionsV3\n\n\nclass LuisRecognizer(Recognizer):\n    \"\"\"\n    A LUIS based implementation of <see cref=\"IRecognizer\"/>.\n    \"\"\"\n\n    # The value type for a LUIS trace activity.\n    luis_trace_type: str = \"https://www.luis.ai/schemas/trace\"\n\n    # The context label for a LUIS trace activity.\n    luis_trace_label: str = \"Luis Trace\"\n\n    def __init__(\n        self,\n        application: Union[LuisApplication, str],\n        prediction_options: Union[\n            LuisRecognizerOptionsV2, LuisRecognizerOptionsV3, LuisPredictionOptions\n        ] = None,\n        include_api_results: bool = False,\n    ):\n        \"\"\"Initializes a new instance of the :class:`LuisRecognizer` class.\n\n        :param application: The LUIS application to use to recognize text.\n        :type application: :class:`LuisApplication`\n        :param prediction_options: The LUIS prediction options to use, defaults to None.\n        :type prediction_options: :class:`LuisPredictionOptions`, optional\n        :param include_api_results: True to include raw LUIS API response, defaults to False.\n        :type include_api_results: bool, optional\n        :raises TypeError:\n        \"\"\"\n\n        if isinstance(application, LuisApplication):\n            self._application = application\n        elif isinstance(application, str):\n            self._application = LuisApplication.from_application_endpoint(application)\n        else:\n            raise TypeError(\n                \"LuisRecognizer.__init__(): application is not an instance of LuisApplication or str.\"\n            )\n\n        self._options = prediction_options or LuisPredictionOptions()\n        self._include_api_results = include_api_results or (\n            prediction_options.include_api_results\n            if isinstance(\n                prediction_options, (LuisRecognizerOptionsV3, LuisRecognizerOptionsV2)\n            )\n            else False\n        )\n\n        self.telemetry_client = self._options.telemetry_client\n        self.log_personal_information = self._options.log_personal_information\n\n    @staticmethod\n    def top_intent(\n        results: RecognizerResult, default_intent: str = \"None\", min_score: float = 0.0\n    ) -> str:\n        \"\"\"Returns the name of the top scoring intent from a set of LUIS results.\n\n        :param results: Result set to be searched.\n        :type results: :class:`botbuilder.core.RecognizerResult`\n        :param default_intent: Intent name to return should a top intent be found, defaults to None.\n        :type default_intent: str, optional\n        :param min_score: Minimum score needed for an intent to be considered as a top intent. If all intents in\n        the set are below this threshold then the `defaultIntent` will be returned, defaults to 0.0.\n        :type min_score: float, optional\n        :raises TypeError:\n        :return: The top scoring intent name.\n        :rtype: str\n        \"\"\"\n\n        if results is None:\n            raise TypeError(\"LuisRecognizer.top_intent(): results cannot be None.\")\n\n        top_intent: str = None\n        top_score: float = -1.0\n        if results.intents:\n            for intent_name, intent_score in results.intents.items():\n                score = intent_score.score\n                if score > top_score and score >= min_score:\n                    top_intent = intent_name\n                    top_score = score\n\n        return top_intent or default_intent\n\n    async def recognize(  # pylint: disable=arguments-differ\n        self,\n        turn_context: TurnContext,\n        telemetry_properties: Dict[str, str] = None,\n        telemetry_metrics: Dict[str, float] = None,\n        luis_prediction_options: LuisPredictionOptions = None,\n    ) -> RecognizerResult:\n        \"\"\"Return results of the analysis (Suggested actions and intents).\n\n        :param turn_context: Context object containing information for a single turn of conversation with a user.\n        :type turn_context: :class:`botbuilder.core.TurnContext`\n        :param telemetry_properties: Additional properties to be logged to telemetry with the LuisResult event, defaults\n         to None.\n        :type telemetry_properties: :class:`typing.Dict[str, str]`, optional\n        :param telemetry_metrics: Additional metrics to be logged to telemetry with the LuisResult event, defaults to\n         None.\n        :type telemetry_metrics: :class:`typing.Dict[str, float]`, optional\n        :return: The LUIS results of the analysis of the current message text in the current turn's context activity.\n        :rtype: :class:`botbuilder.core.RecognizerResult`\n        \"\"\"\n\n        return await self._recognize_internal(\n            turn_context,\n            telemetry_properties,\n            telemetry_metrics,\n            luis_prediction_options,\n        )\n\n    def on_recognizer_result(\n        self,\n        recognizer_result: RecognizerResult,\n        turn_context: TurnContext,\n        telemetry_properties: Dict[str, str] = None,\n        telemetry_metrics: Dict[str, float] = None,\n    ):\n        \"\"\"Invoked prior to a LuisResult being logged.\n\n        :param recognizer_result: The Luis Results for the call.\n        :type recognizer_result: :class:`botbuilder.core.RecognizerResult`\n        :param turn_context: Context object containing information for a single turn of conversation with a user.\n        :type turn_context: :class:`botbuilder.core.TurnContext`\n        :param telemetry_properties: Additional properties to be logged to telemetry with the LuisResult event, defaults\n         to None.\n        :type telemetry_properties: :class:`typing.Dict[str, str]`, optional\n        :param telemetry_metrics: Additional metrics to be logged to telemetry with the LuisResult event, defaults\n         to None.\n        :type telemetry_metrics: :class:`typing.Dict[str, float]`, optional\n        \"\"\"\n\n        properties = self.fill_luis_event_properties(\n            recognizer_result, turn_context, telemetry_properties\n        )\n\n        # Track the event\n        self.telemetry_client.track_event(\n            LuisTelemetryConstants.luis_result, properties, telemetry_metrics\n        )\n\n    @staticmethod\n    def _get_top_k_intent_score(\n        intent_names: List[str],\n        intents: Dict[str, IntentScore],\n        index: int,  # pylint: disable=unused-argument\n    ) -> Tuple[str, str]:\n        intent_name = \"\"\n        intent_score = \"0.00\"\n        if intent_names:\n            intent_name = intent_names[0]\n            if intents[intent_name] is not None:\n                intent_score = \"{:.2f}\".format(intents[intent_name].score)\n\n        return intent_name, intent_score\n\n    def fill_luis_event_properties(\n        self,\n        recognizer_result: RecognizerResult,\n        turn_context: TurnContext,\n        telemetry_properties: Dict[str, str] = None,\n    ) -> Dict[str, str]:\n        \"\"\"Fills the event properties for LuisResult event for telemetry.\n        These properties are logged when the recognizer is called.\n\n        :param recognizer_result: Last activity sent from user.\n        :type recognizer_result: :class:`botbuilder.core.RecognizerResult`\n        :param turn_context: Context object containing information for a single turn of conversation with a user.\n        :type turn_context: :class:`botbuilder.core.TurnContext`\n        :param telemetry_properties: Additional properties to be logged to telemetry with the LuisResult event,\n         defaults to None\n        :param telemetry_properties: :class:`typing.Dict[str, str]`, optional\n        :return: A dictionary that is sent as \"Properties\" to :func:`botbuilder.core.BotTelemetryClient.track_event`\n        method for the BotMessageSend event.\n        :rtype: `typing.Dict[str, str]`\n        \"\"\"\n\n        intents = recognizer_result.intents\n        top_two_intents = (\n            sorted(intents.keys(), key=lambda k: intents[k].score, reverse=True)[:2]\n            if intents\n            else []\n        )\n\n        intent_name, intent_score = LuisRecognizer._get_top_k_intent_score(\n            top_two_intents, intents, index=0\n        )\n        intent2_name, intent2_score = LuisRecognizer._get_top_k_intent_score(\n            top_two_intents, intents, index=1\n        )\n\n        # Add the intent score and conversation id properties\n        properties: Dict[str, str] = {\n            LuisTelemetryConstants.application_id_property: self._application.application_id,\n            LuisTelemetryConstants.intent_property: intent_name,\n            LuisTelemetryConstants.intent_score_property: intent_score,\n            LuisTelemetryConstants.intent2_property: intent2_name,\n            LuisTelemetryConstants.intent_score2_property: intent2_score,\n            LuisTelemetryConstants.from_id_property: turn_context.activity.from_property.id,\n        }\n\n        sentiment = recognizer_result.properties.get(\"sentiment\")\n        if sentiment is not None and isinstance(sentiment, Dict):\n            label = sentiment.get(\"label\")\n            if label is not None:\n                properties[LuisTelemetryConstants.sentiment_label_property] = str(label)\n\n            score = sentiment.get(\"score\")\n            if score is not None:\n                properties[LuisTelemetryConstants.sentiment_score_property] = str(score)\n\n        entities = None\n        if recognizer_result.entities is not None:\n            entities = json.dumps(recognizer_result.entities)\n        properties[LuisTelemetryConstants.entities_property] = entities\n\n        # Use the LogPersonalInformation flag to toggle logging PII data, text is a common example\n        if self.log_personal_information and turn_context.activity.text:\n            properties[\n                LuisTelemetryConstants.question_property\n            ] = turn_context.activity.text\n\n        # Additional Properties can override \"stock\" properties.\n        if telemetry_properties is not None:\n            for key in telemetry_properties:\n                properties[key] = telemetry_properties[key]\n\n        return properties\n\n    async def _recognize_internal(\n        self,\n        turn_context: TurnContext,\n        telemetry_properties: Dict[str, str],\n        telemetry_metrics: Dict[str, float],\n        luis_prediction_options: Union[\n            LuisPredictionOptions, LuisRecognizerOptionsV2, LuisRecognizerOptionsV3\n        ] = None,\n    ) -> RecognizerResult:\n\n        BotAssert.context_not_none(turn_context)\n\n        if turn_context.activity.type != ActivityTypes.message:\n            return None\n\n        utterance: str = turn_context.activity.text if turn_context.activity is not None else None\n        recognizer_result: RecognizerResult = None\n\n        if luis_prediction_options:\n            options = luis_prediction_options\n        else:\n            options = self._options\n\n        if not utterance or utterance.isspace():\n            recognizer_result = RecognizerResult(\n                text=utterance\n            )\n        else:\n\n            luis_recognizer = self._build_recognizer(options)\n            recognizer_result = await luis_recognizer.recognizer_internal(turn_context)\n\n        # Log telemetry\n        self.on_recognizer_result(\n            recognizer_result, turn_context, telemetry_properties, telemetry_metrics\n        )\n\n        return recognizer_result\n\n    def _merge_options(\n        self,\n        user_defined_options: Union[\n            LuisRecognizerOptionsV3, LuisRecognizerOptionsV2, LuisPredictionOptions\n        ],\n    ) -> LuisPredictionOptions:\n        merged_options = LuisPredictionOptions()\n        merged_options.__dict__.update(user_defined_options.__dict__)\n        return merged_options\n\n    def _build_recognizer(\n        self,\n        luis_prediction_options: Union[\n            LuisRecognizerOptionsV3, LuisRecognizerOptionsV2, LuisPredictionOptions\n        ],\n    ):\n        if isinstance(luis_prediction_options, LuisRecognizerOptionsV3):\n            return LuisRecognizerV3(self._application, luis_prediction_options)\n        if isinstance(luis_prediction_options, LuisRecognizerOptionsV2):\n            return LuisRecognizerV3(self._application, luis_prediction_options)\n\n        recognizer_options = LuisRecognizerOptionsV2(\n            luis_prediction_options.bing_spell_check_subscription_key,\n            luis_prediction_options.include_all_intents,\n            luis_prediction_options.include_instance_data,\n            luis_prediction_options.log,\n            luis_prediction_options.spell_check,\n            luis_prediction_options.staging,\n            luis_prediction_options.timeout,\n            luis_prediction_options.timezone_offset,\n            self._include_api_results,\n            luis_prediction_options.telemetry_client,\n            luis_prediction_options.log_personal_information,\n        )\n        return LuisRecognizerV2(self._application, recognizer_options)\n", "path": "libraries/botbuilder-ai/botbuilder/ai/luis/luis_recognizer.py"}]}