Repository: microsoft/botbuilder-python
Pull request #228 (2019-06-26 02:29:39)

Tests for activity prompt. 
Fixes on prompt, prompt_validator_context, activity_prompt and test_adapter


Patch:
diff --git a/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/about.py b/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/about.py
index f897d2e3..bb0fd5e9 100644
--- a/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/about.py
+++ b/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/about.py
@@ -1,13 +1,12 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Bot Framework Application Insights integration package info."""
-
-import os
-
-__title__ = 'botbuilder-applicationinsights'
-__version__ = os.environ["packageVersion"] if "packageVersion" in os.environ else "4.4.0b1"
-__uri__ = 'https://www.github.com/Microsoft/botbuilder-python'
-__author__ = 'Microsoft'
-__description__ = 'Microsoft Bot Framework Bot Builder'
-__summary__ = 'Microsoft Bot Framework Bot Builder SDK for Python.'
-__license__ = 'MIT'
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+
+import os
+
+__title__ = 'botbuilder-applicationinsights'
+__version__ = os.environ["packageVersion"] if "packageVersion" in os.environ else "4.4.0b1"
+__uri__ = 'https://www.github.com/Microsoft/botbuilder-python'
+__author__ = 'Microsoft'
+__description__ = 'Microsoft Bot Framework Bot Builder'
+__summary__ = 'Microsoft Bot Framework Bot Builder SDK for Python.'
+__license__ = 'MIT'
diff --git a/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/appinsights_bot_telemetry_client.py b/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/appinsights_bot_telemetry_client.py
new file mode 100644
index 00000000..da097991
--- /dev/null
+++ b/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/appinsights_bot_telemetry_client.py
@@ -0,0 +1,123 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+import sys
+import traceback
+from applicationinsights import TelemetryClient
+from botbuilder.core.bot_telemetry_client import BotTelemetryClient, TelemetryDataPointType
+from typing import Dict
+
+class AppinsightsBotTelemetryClient(BotTelemetryClient):
+
+    def __init__(self, instrumentation_key:str):
+        self._instrumentation_key = instrumentation_key
+
+        self._context = TelemetryContext()
+        context.instrumentation_key = self._instrumentation_key
+        # context.user.id = 'BOTID'        # telemetry_channel.context.session.
+        # context.session.id = 'BOTSESSION'
+
+        # set up channel with context
+        self._channel = TelemetryChannel(context)
+        # telemetry_channel.context.properties['my_property'] = 'my_value'
+
+        self._client = TelemetryClient(self._instrumentation_key, self._channel)
+        
+    
+    def track_pageview(self, name: str, url:str, duration: int = 0, properties : Dict[str, object]=None, 
+                        measurements: Dict[str, object]=None) -> None:
+        """
+        Send information about the page viewed in the application (a web page for instance).
+        :param name: the name of the page that was viewed.
+        :param url: the URL of the page that was viewed.
+        :param duration: the duration of the page view in milliseconds. (defaults to: 0)
+        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
+        :param measurements: the set of custom measurements the client wants to attach to this data item. (defaults to: None)
+        """
+        self._client.track_pageview(name, url, duration, properties, measurements)
+    
+    def track_exception(self, type_exception: type = None, value : Exception =None, tb : traceback =None, 
+                        properties: Dict[str, object]=None, measurements: Dict[str, object]=None) -> None:
+        """ 
+        Send information about a single exception that occurred in the application.
+        :param type_exception: the type of the exception that was thrown.
+        :param value: the exception that the client wants to send.
+        :param tb: the traceback information as returned by :func:`sys.exc_info`.
+        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
+        :param measurements: the set of custom measurements the client wants to attach to this data item. (defaults to: None)
+        """
+        self._client.track_exception(type_exception, value, tb, properties, measurements)
+
+    def track_event(self, name: str, properties: Dict[str, object] = None, 
+                    measurements: Dict[str, object] = None) -> None:
+        """ 
+        Send information about a single event that has occurred in the context of the application.
+        :param name: the data to associate to this event.
+        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
+        :param measurements: the set of custom measurements the client wants to attach to this data item. (defaults to: None)
+        """
+        self._client.track_event(name, properties, measurements)
+
+    def track_metric(self, name: str, value: float, type: TelemetryDataPointType =None, 
+                    count: int =None, min: float=None, max: float=None, std_dev: float=None,
+                    properties: Dict[str, object]=None) -> NotImplemented:
+        """
+        Send information about a single metric data point that was captured for the application.
+        :param name: The name of the metric that was captured.
+        :param value: The value of the metric that was captured.
+        :param type: The type of the metric. (defaults to: TelemetryDataPointType.aggregation`)
+        :param count: the number of metrics that were aggregated into this data point. (defaults to: None)
+        :param min: the minimum of all metrics collected that were aggregated into this data point. (defaults to: None)
+        :param max: the maximum of all metrics collected that were aggregated into this data point. (defaults to: None)
+        :param std_dev: the standard deviation of all metrics collected that were aggregated into this data point. (defaults to: None)
+        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
+        """
+        self._client.track_metric(name, value, type, count, min, max, std_dev, properties)
+
+    def track_trace(self, name: str, properties: Dict[str, object]=None, severity=None):
+        """
+        Sends a single trace statement.
+        :param name: the trace statement.\n
+        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)\n
+        :param severity: the severity level of this trace, one of DEBUG, INFO, WARNING, ERROR, CRITICAL
+        """
+        self._client.track_trace(name, properties, severity)
+
+    def track_request(self, name: str, url: str, success: bool, start_time: str=None, 
+                    duration: int=None, response_code: str =None, http_method: str=None, 
+                    properties: Dict[str, object]=None, measurements: Dict[str, object]=None, 
+                    request_id: str=None):
+        """
+        Sends a single request that was captured for the application.
+        :param name: The name for this request. All requests with the same name will be grouped together.
+        :param url: The actual URL for this request (to show in individual request instances).
+        :param success: True if the request ended in success, False otherwise.
+        :param start_time: the start time of the request. The value should look the same as the one returned by :func:`datetime.isoformat()` (defaults to: None)
+        :param duration: the number of milliseconds that this request lasted. (defaults to: None)
+        :param response_code: the response code that this request returned. (defaults to: None)
+        :param http_method: the HTTP method that triggered this request. (defaults to: None)
+        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
+        :param measurements: the set of custom measurements the client wants to attach to this data item. (defaults to: None)
+        :param request_id: the id for this request. If None, a new uuid will be generated. (defaults to: None)
+        """
+        self._client.track_request(name, url, success, start_time, duration, response_code, http_method, properties,
+                                    measurements, request_id)
+
+    def track_dependency(self, name:str, data:str, type:str=None, target:str=None, duration:int=None, 
+                        success:bool=None, result_code:str=None, properties:Dict[str, object]=None, 
+                        measurements:Dict[str, object]=None, dependency_id:str=None):
+        """
+        Sends a single dependency telemetry that was captured for the application.
+        :param name: the name of the command initiated with this dependency call. Low cardinality value. Examples are stored procedure name and URL path template.
+        :param data: the command initiated by this dependency call. Examples are SQL statement and HTTP URL with all query parameters.
+        :param type: the dependency type name. Low cardinality value for logical grouping of dependencies and interpretation of other fields like commandName and resultCode. Examples are SQL, Azure table, and HTTP. (default to: None)
+        :param target: the target site of a dependency call. Examples are server name, host address. (default to: None)
+        :param duration: the number of milliseconds that this dependency call lasted. (defaults to: None)
+        :param success: true if the dependency call ended in success, false otherwise. (defaults to: None)
+        :param result_code: the result code of a dependency call. Examples are SQL error code and HTTP status code. (defaults to: None)
+        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
+        :param measurements: the set of custom measurements the client wants to attach to this data item. (defaults to: None)
+        :param id: the id for this dependency call. If None, a new uuid will be generated. (defaults to: None)
+        """
+        self._client.track_dependency(name, data, type, target, duration, success, result_code, properties,
+                                        measurements, dependency_id)
+
diff --git a/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/application_insights_telemetry_client.py b/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/application_insights_telemetry_client.py
index 9f3921cf..db2b6f89 100644
--- a/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/application_insights_telemetry_client.py
+++ b/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/application_insights_telemetry_client.py
@@ -1,176 +1,145 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Application Insights Telemetry Processor for Bots."""
-
-import traceback
-from typing import Dict
-from applicationinsights import TelemetryClient
-from botbuilder.core.bot_telemetry_client import BotTelemetryClient, TelemetryDataPointType
-from .integration_post_data import IntegrationPostData
-
-# pylint: disable=line-too-long
-def bot_telemetry_processor(data, context) -> bool:
-    """ Application Insights Telemetry Processor for Bot
-    Traditional Web user and session ID's don't apply for Bots.  This processor
-    replaces the identifiers to be consistent with Bot Framework's notion of
-    user and session id's.
-
-    Each event that gets logged (with this processor added) will contain additional
-    properties.
-
-    The following properties are replaced:
-     - context.user.id    - The user ID that Application Insights uses to identify
-                            a unique user.
-     - context.session.id - The session ID that APplication Insights uses to
-                            identify a unique session.
-
-     In addition, the additional data properties are added:
-     - activityId - The Bot Framework's Activity ID which represents a unique
-                    message identifier.
-     - channelId - The Bot Framework "Channel" (ie, slack/facebook/directline/etc)
-     - activityType - The Bot Framework message classification (ie, message)
-
-    :param data: Data from Application Insights
-    :type data: telemetry item
-    :param context: Context from Application Insights
-    :type context: context object
-    :returns:  bool -- determines if the event is passed to the server (False = Filtered).
-    """
-    post_data = IntegrationPostData().activity_json
-    if post_data is None:
-        # If there is no body (not a BOT request or not configured correctly).
-        # We *could* filter here, but we're allowing event to go through.
-        return True
-
-    # Override session and user id
-    from_prop = post_data['from'] if 'from' in post_data else None
-    user_id = from_prop['id'] if from_prop is not None else None
-    channel_id = post_data['channelId'] if 'channelId' in post_data else None
-    conversation = post_data['conversation'] if 'conversation' in post_data else None
-    conversation_id = conversation['id'] if 'id' in conversation else None
-    context.user.id = channel_id + user_id
-    context.session.id = conversation_id
-
-    # Additional bot-specific properties
-    if 'id' in post_data:
-        data.properties["activityId"] = post_data['id']
-    if 'channelId' in post_data:
-        data.properties["channelId"] = post_data['channelId']
-    if 'type' in post_data:
-        data.properties["activityType"] = post_data['type']
-    return True
-
-
-class ApplicationInsightsTelemetryClient(BotTelemetryClient):
-    """Application Insights Telemetry Client."""
-    def __init__(self, instrumentation_key: str, telemetry_client: TelemetryClient = None):
-        self._instrumentation_key = instrumentation_key
-        self._client = telemetry_client if telemetry_client is not None \
-            else TelemetryClient(self._instrumentation_key)
-        # Telemetry Processor
-        self._client.add_telemetry_processor(bot_telemetry_processor)
-
-
-    def track_pageview(self, name: str, url: str, duration: int = 0, properties: Dict[str, object] = None,
-                       measurements: Dict[str, object] = None) -> None:
-        """
-        Send information about the page viewed in the application (a web page for instance).
-        :param name: the name of the page that was viewed.
-        :param url: the URL of the page that was viewed.
-        :param duration: the duration of the page view in milliseconds. (defaults to: 0)
-        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
-        :param measurements: the set of custom measurements the client wants to attach to this data item. (defaults to: None)
-        """
-        self._client.track_pageview(name, url, duration, properties, measurements)
-
-    def track_exception(self, exception_type: type = None, value: Exception = None, tb: traceback = None,
-                        properties: Dict[str, object] = None, measurements: Dict[str, object] = None) -> None:
-        """
-        Send information about a single exception that occurred in the application.
-        :param exception_type: the type of the exception that was thrown.
-        :param value: the exception that the client wants to send.
-        :param tb: the traceback information as returned by :func:`sys.exc_info`.
-        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
-        :param measurements: the set of custom measurements the client wants to attach to this data item. (defaults to: None)
-        """
-        self._client.track_exception(exception_type, value, tb, properties, measurements)
-
-    def track_event(self, name: str, properties: Dict[str, object] = None,
-                    measurements: Dict[str, object] = None) -> None:
-        """
-        Send information about a single event that has occurred in the context of the application.
-        :param name: the data to associate to this event.
-        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
-        :param measurements: the set of custom measurements the client wants to attach to this data item. (defaults to: None)
-        """
-        self._client.track_event(name, properties=properties, measurements=measurements)
-
-    def track_metric(self, name: str, value: float, tel_type: TelemetryDataPointType = None,
-                     count: int = None, min_val: float = None, max_val: float = None, std_dev: float = None,
-                     properties: Dict[str, object] = None) -> NotImplemented:
-        """
-        Send information about a single metric data point that was captured for the application.
-        :param name: The name of the metric that was captured.
-        :param value: The value of the metric that was captured.
-        :param tel_type: The type of the metric. (defaults to: TelemetryDataPointType.aggregation`)
-        :param count: the number of metrics that were aggregated into this data point. (defaults to: None)
-        :param min_val: the minimum of all metrics collected that were aggregated into this data point. (defaults to: None)
-        :param max_val: the maximum of all metrics collected that were aggregated into this data point. (defaults to: None)
-        :param std_dev: the standard deviation of all metrics collected that were aggregated into this data point. (defaults to: None)
-        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
-        """
-        self._client.track_metric(name, value, tel_type, count, min_val, max_val, std_dev, properties)
-
-    def track_trace(self, name: str, properties: Dict[str, object] = None, severity=None):
-        """
-        Sends a single trace statement.
-        :param name: the trace statement.
-        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
-        :param severity: the severity level of this trace, one of DEBUG, INFO, WARNING, ERROR, CRITICAL
-        """
-        self._client.track_trace(name, properties, severity)
-
-    def track_request(self, name: str, url: str, success: bool, start_time: str = None,
-                      duration: int = None, response_code: str = None, http_method: str = None,
-                      properties: Dict[str, object] = None, measurements: Dict[str, object] = None,
-                      request_id: str = None):
-        """
-        Sends a single request that was captured for the application.
-        :param name: The name for this request. All requests with the same name will be grouped together.
-        :param url: The actual URL for this request (to show in individual request instances).
-        :param success: True if the request ended in success, False otherwise.
-        :param start_time: the start time of the request. The value should look the same as the one returned by :func:`datetime.isoformat()` (defaults to: None)
-        :param duration: the number of milliseconds that this request lasted. (defaults to: None)
-        :param response_code: the response code that this request returned. (defaults to: None)
-        :param http_method: the HTTP method that triggered this request. (defaults to: None)
-        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
-        :param measurements: the set of custom measurements the client wants to attach to this data item. (defaults to: None)
-        :param request_id: the id for this request. If None, a new uuid will be generated. (defaults to: None)
-        """
-        self._client.track_request(name, url, success, start_time, duration, response_code, http_method, properties,
-                                   measurements, request_id)
-
-    def track_dependency(self, name: str, data: str, type_name: str = None, target: str = None, duration: int = None,
-                         success: bool = None, result_code: str = None, properties: Dict[str, object] = None,
-                         measurements: Dict[str, object] = None, dependency_id: str = None):
-        """
-        Sends a single dependency telemetry that was captured for the application.
-        :param name: the name of the command initiated with this dependency call. Low cardinality value. Examples are stored procedure name and URL path template.
-        :param data: the command initiated by this dependency call. Examples are SQL statement and HTTP URL with all query parameters.
-        :param type_name: the dependency type name. Low cardinality value for logical grouping of dependencies and interpretation of other fields like commandName and resultCode. Examples are SQL, Azure table, and HTTP. (default to: None)
-        :param target: the target site of a dependency call. Examples are server name, host address. (default to: None)
-        :param duration: the number of milliseconds that this dependency call lasted. (defaults to: None)
-        :param success: true if the dependency call ended in success, false otherwise. (defaults to: None)
-        :param result_code: the result code of a dependency call. Examples are SQL error code and HTTP status code. (defaults to: None)
-        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
-        :param measurements: the set of custom measurements the client wants to attach to this data item. (defaults to: None)
-        :param id: the id for this dependency call. If None, a new uuid will be generated. (defaults to: None)
-        """
-        self._client.track_dependency(name, data, type_name, target, duration, success, result_code, properties,
-                                      measurements, dependency_id)
-
-    def flush(self):
-        """Flushes data in the queue. Data in the queue will be sent either immediately irrespective of what sender is
-        being used.
-        """
-        self._client.flush()
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+import sys
+import traceback
+from applicationinsights import TelemetryClient
+from botbuilder.core.bot_telemetry_client import BotTelemetryClient, TelemetryDataPointType
+from typing import Dict
+from .integration_post_data import IntegrationPostData
+
+def bot_telemetry_processor(data, context):
+    post_data = IntegrationPostData().activity_json
+    if post_data is None:
+        return
+    # Override session and user id
+    from_prop = post_data['from'] if 'from' in post_data else None
+    user_id = from_prop['id'] if from_prop != None else None
+    channel_id = post_data['channelId'] if 'channelId' in post_data else None
+    conversation = post_data['conversation'] if 'conversation' in post_data else None
+    conversation_id = conversation['id'] if 'id' in conversation else None
+    context.user.id = channel_id + user_id
+    context.session.id = conversation_id
+
+    # Additional bot-specific properties
+    if 'id' in post_data:
+        data.properties["activityId"] = post_data['id'] 
+    if 'channelId' in post_data:
+        data.properties["channelId"] = post_data['channelId'] 
+    if 'type' in post_data:
+        data.properties["activityType"] = post_data['type']
+
+
+class ApplicationInsightsTelemetryClient(BotTelemetryClient):
+    
+    def __init__(self, instrumentation_key:str, telemetry_client: TelemetryClient = None):
+        self._instrumentation_key = instrumentation_key
+        self._client = telemetry_client if telemetry_client != None else TelemetryClient(self._instrumentation_key)
+        # Telemetry Processor
+        self._client.add_telemetry_processor(bot_telemetry_processor)
+        
+
+    def track_pageview(self, name: str, url:str, duration: int = 0, properties : Dict[str, object]=None, 
+                        measurements: Dict[str, object]=None) -> None:
+        """
+        Send information about the page viewed in the application (a web page for instance).
+        :param name: the name of the page that was viewed.
+        :param url: the URL of the page that was viewed.
+        :param duration: the duration of the page view in milliseconds. (defaults to: 0)
+        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
+        :param measurements: the set of custom measurements the client wants to attach to this data item. (defaults to: None)
+        """
+        self._client.track_pageview(name, url, duration, properties, measurements)
+    
+    def track_exception(self, type_exception: type = None, value : Exception =None, tb : traceback =None, 
+                        properties: Dict[str, object]=None, measurements: Dict[str, object]=None) -> None:
+        """ 
+        Send information about a single exception that occurred in the application.
+        :param type_exception: the type of the exception that was thrown.
+        :param value: the exception that the client wants to send.
+        :param tb: the traceback information as returned by :func:`sys.exc_info`.
+        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
+        :param measurements: the set of custom measurements the client wants to attach to this data item. (defaults to: None)
+        """
+        self._client.track_exception(type_exception, value, tb, properties, measurements)
+
+    def track_event(self, name: str, properties: Dict[str, object] = None, 
+                    measurements: Dict[str, object] = None) -> None:
+        """ 
+        Send information about a single event that has occurred in the context of the application.
+        :param name: the data to associate to this event.
+        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
+        :param measurements: the set of custom measurements the client wants to attach to this data item. (defaults to: None)
+        """
+        self._client.track_event(name, properties, measurements)
+
+    def track_metric(self, name: str, value: float, type: TelemetryDataPointType =None, 
+                    count: int =None, min: float=None, max: float=None, std_dev: float=None,
+                    properties: Dict[str, object]=None) -> NotImplemented:
+        """
+        Send information about a single metric data point that was captured for the application.
+        :param name: The name of the metric that was captured.
+        :param value: The value of the metric that was captured.
+        :param type: The type of the metric. (defaults to: TelemetryDataPointType.aggregation`)
+        :param count: the number of metrics that were aggregated into this data point. (defaults to: None)
+        :param min: the minimum of all metrics collected that were aggregated into this data point. (defaults to: None)
+        :param max: the maximum of all metrics collected that were aggregated into this data point. (defaults to: None)
+        :param std_dev: the standard deviation of all metrics collected that were aggregated into this data point. (defaults to: None)
+        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
+        """
+        self._client.track_metric(name, value, type, count, min, max, std_dev, properties)
+
+    def track_trace(self, name: str, properties: Dict[str, object]=None, severity=None):
+        """
+        Sends a single trace statement.
+        :param name: the trace statement.\n
+        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)\n
+        :param severity: the severity level of this trace, one of DEBUG, INFO, WARNING, ERROR, CRITICAL
+        """
+        self._client.track_trace(name, properties, severity)
+
+    def track_request(self, name: str, url: str, success: bool, start_time: str=None, 
+                    duration: int=None, response_code: str =None, http_method: str=None, 
+                    properties: Dict[str, object]=None, measurements: Dict[str, object]=None, 
+                    request_id: str=None):
+        """
+        Sends a single request that was captured for the application.
+        :param name: The name for this request. All requests with the same name will be grouped together.
+        :param url: The actual URL for this request (to show in individual request instances).
+        :param success: True if the request ended in success, False otherwise.
+        :param start_time: the start time of the request. The value should look the same as the one returned by :func:`datetime.isoformat()` (defaults to: None)
+        :param duration: the number of milliseconds that this request lasted. (defaults to: None)
+        :param response_code: the response code that this request returned. (defaults to: None)
+        :param http_method: the HTTP method that triggered this request. (defaults to: None)
+        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
+        :param measurements: the set of custom measurements the client wants to attach to this data item. (defaults to: None)
+        :param request_id: the id for this request. If None, a new uuid will be generated. (defaults to: None)
+        """
+        self._client.track_request(name, url, success, start_time, duration, response_code, http_method, properties,
+                                    measurements, request_id)
+
+    def track_dependency(self, name:str, data:str, type:str=None, target:str=None, duration:int=None, 
+                        success:bool=None, result_code:str=None, properties:Dict[str, object]=None, 
+                        measurements:Dict[str, object]=None, dependency_id:str=None):
+        """
+        Sends a single dependency telemetry that was captured for the application.
+        :param name: the name of the command initiated with this dependency call. Low cardinality value. Examples are stored procedure name and URL path template.
+        :param data: the command initiated by this dependency call. Examples are SQL statement and HTTP URL with all query parameters.
+        :param type: the dependency type name. Low cardinality value for logical grouping of dependencies and interpretation of other fields like commandName and resultCode. Examples are SQL, Azure table, and HTTP. (default to: None)
+        :param target: the target site of a dependency call. Examples are server name, host address. (default to: None)
+        :param duration: the number of milliseconds that this dependency call lasted. (defaults to: None)
+        :param success: true if the dependency call ended in success, false otherwise. (defaults to: None)
+        :param result_code: the result code of a dependency call. Examples are SQL error code and HTTP status code. (defaults to: None)
+        :param properties: the set of custom properties the client wants attached to this data item. (defaults to: None)
+        :param measurements: the set of custom measurements the client wants to attach to this data item. (defaults to: None)
+        :param id: the id for this dependency call. If None, a new uuid will be generated. (defaults to: None)
+        """
+        self._client.track_dependency(name, data, type, target, duration, success, result_code, properties,
+                                        measurements, dependency_id)
+
+    def flush(self):
+        """Flushes data in the queue. Data in the queue will be sent either immediately irrespective of what sender is
+        being used.
+        """
+        self._client.flush()
+
+
diff --git a/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/django/__init__.py b/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/django/__init__.py
index df9279f1..71248f5d 100644
--- a/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/django/__init__.py
+++ b/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/django/__init__.py
@@ -1,10 +1,9 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Django Application Insights package."""
-
-from .bot_telemetry_middleware import BotTelemetryMiddleware, retrieve_bot_body
-
-__all__ = [
-    "BotTelemetryMiddleware",
-    "retrieve_bot_body"
-]
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+
+from .bot_telemetry_middleware import BotTelemetryMiddleware, retrieve_bot_body
+
+__all__ = [
+    "BotTelemetryMiddleware",
+    "retrieve_bot_body"
+]
diff --git a/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/django/bot_telemetry_middleware.py b/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/django/bot_telemetry_middleware.py
index ff06549a..59db783b 100644
--- a/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/django/bot_telemetry_middleware.py
+++ b/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/django/bot_telemetry_middleware.py
@@ -1,47 +1,43 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Bot Telemetry Middleware."""
-
-from threading import current_thread
-
-# Map of thread id => POST body text
-_REQUEST_BODIES = {}
-
-def retrieve_bot_body():
-    """ retrieve_bot_body
-    Retrieve the POST body text from temporary cache.
-    The POST body corresponds with the thread id and should resides in
-    cache just for lifetime of request.
-    """
-    result = _REQUEST_BODIES.pop(current_thread().ident, None)
-    return result
-
-class BotTelemetryMiddleware():
-    """
-    Save off the POST body to later populate bot-specific properties to
-    add to Application Insights.
-
-    Example activating MIDDLEWARE in Django settings:
-    MIDDLEWARE = [
-        # Ideally add somewhere near top
-        'botbuilder.applicationinsights.django.BotTelemetryMiddleware',
-        ...
-        ]
-    """
-    def __init__(self, get_response):
-        self.get_response = get_response
-
-    def __call__(self, request):
-        self.process_request(request)
-        return self.get_response(request)
-
-    def process_request(self, request) -> bool:
-        """Process the incoming Django request."""
-        # Bot Service doesn't handle anything over 256k
-        # TODO: Add length check
-        body_unicode = request.body.decode('utf-8') if request.method == "POST" else None
-        # Sanity check JSON
-        if body_unicode is not None:
-            # Integration layer expecting just the json text.
-            _REQUEST_BODIES[current_thread().ident] = body_unicode
-        return True
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+import sys
+import json
+from threading import current_thread
+
+# Map of thread id => POST body text
+_request_bodies = {}
+
+def retrieve_bot_body():
+    """ retrieve_bot_body
+    Retrieve the POST body text from temporary cache.
+    The POST body corresponds with the thread id and should resides in 
+    cache just for lifetime of request.
+
+    TODO: Add cleanup job to kill orphans
+    """
+    result = _request_bodies.pop(current_thread().ident, None)
+    return result
+
+class BotTelemetryMiddleware():
+    """
+    Save off the POST body to later populate bot properties
+    """
+    def __init__(self, get_response):
+        self.get_response = get_response
+
+    def __call__(self, request):
+        self.process_request(request)
+        return self.get_response(request)
+
+    def process_request(self, request):
+        body_unicode = request.body.decode('utf-8') if request.method == "POST" else None
+        # Sanity check JSON
+        if body_unicode != None:
+            try:
+                body = json.loads(body_unicode)
+            except:
+                return
+            # Integration layer expecting just the json text.
+            _request_bodies[current_thread().ident] = body_unicode
+
+    
diff --git a/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/flask/__init__.py b/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/flask/__init__.py
deleted file mode 100644
index 5b35a6c9..00000000
--- a/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/flask/__init__.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Flask Application Insights package."""
-
-from .flask_telemetry_middleware import BotTelemetryMiddleware, retrieve_flask_body
-
-__all__ = [
-    "BotTelemetryMiddleware",
-    "retrieve_flask_body"
-]
diff --git a/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/flask/flask_telemetry_middleware.py b/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/flask/flask_telemetry_middleware.py
deleted file mode 100644
index e4cbb84d..00000000
--- a/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/flask/flask_telemetry_middleware.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Flask Telemetry Bot Middleware."""
-
-from io import BytesIO
-from threading import current_thread
-
-# Map of thread id => POST body text
-_REQUEST_BODIES = {}
-
-def retrieve_flask_body():
-    """ retrieve_flask_body
-    Retrieve the POST body text from temporary cache.
-    The POST body corresponds with the thread id and should resides in
-    cache just for lifetime of request.
-    """
-    result = _REQUEST_BODIES.pop(current_thread().ident, None)
-    return result
-
-class BotTelemetryMiddleware():
-    """Bot Telemetry Middleware
-    Save off the POST body to later populate bot-specific properties to
-    add to Application Insights.
-
-    Example adding telemetry middleware to Flask:
-       app = Flask(__name__)
-       app.wsgi_app = BotTelemetryMiddleware(app.wsgi_app)
-    """
-    def __init__(self, app):
-        self.app = app
-
-    def __call__(self, environ, start_response):
-        self.process_request(environ)
-        return self.app(environ, start_response)
-
-    def process_request(self, environ) -> bool:
-        """Process the incoming Flask request."""
-        # Bot Service doesn't handle anything over 256k
-        length = int(environ.get('CONTENT_LENGTH', '0'))
-        if length > 256*1024:
-            print(f'request too long - rejected')
-        else:
-            body_bytes = environ['wsgi.input'].read(length)
-            environ['wsgi.input'] = BytesIO(body_bytes)
-            body_unicode = body_bytes.decode('utf-8')
-
-        # Sanity check JSON
-        if body_unicode is not None:
-            # Integration layer expecting just the json text.
-            _REQUEST_BODIES[current_thread().ident] = body_unicode
-        return True
diff --git a/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/integration_post_data.py b/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/integration_post_data.py
index 74013537..bb089140 100644
--- a/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/integration_post_data.py
+++ b/libraries/botbuilder-applicationinsights/botbuilder/applicationinsights/integration_post_data.py
@@ -1,51 +1,76 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Retrieve the POST request body from underlying web framework."""
-
-import sys
-import json
-from botbuilder.applicationinsights.django import retrieve_bot_body
-from botbuilder.applicationinsights.flask import retrieve_flask_body
-
-class IntegrationPostData:
-    """
-    Retrieve the POST body from the underlying framework:
-    - Flask
-    - Django
-    - (soon Tornado?)
-
-    This class:
-    - Detects framework (currently flask or django)
-    - Pulls the current request body as a string
-
-    Usage:
-      botdata = BotTelemetryData()
-      body = botdata.activity_json  # Get current request body as json object
-      activity_id = body[id] # Get the ID from the POST body
-    """
-    def __init__(self):
-        pass
-
-    @property
-    def activity_json(self) -> json:
-        """Retrieve the request body as json (Activity)."""
-        body_text = self.get_request_body()
-        body = json.loads(body_text) if body_text is not None else None
-        return body
-
-    def get_request_body(self) -> str:
-        """Retrieve the request body from flask/django middleware component."""
-        if self.detect_flask():
-            return retrieve_flask_body()
-        else:
-            if self.detect_django():
-                # Retrieve from Middleware cache
-                return retrieve_bot_body()
-
-    def detect_flask(self) -> bool:
-        """Detects if running in flask."""
-        return "flask" in sys.modules
-
-    def detect_django(self) -> bool:
-        """Detects if running in django."""
-        return "django" in sys.modules
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+
+import sys
+import gc
+import imp 
+import json
+from botbuilder.schema import Activity
+from botbuilder.applicationinsights.django import retrieve_bot_body
+
+class IntegrationPostData:
+    """ 
+    Retrieve the POST body from the underlying framework:
+    - Flask
+    - Django
+    - (soon Tornado?)
+
+    This class:
+    - Detects framework (currently flask or django)
+    - Pulls the current request body as a string
+    
+    Usage:
+      botdata = BotTelemetryData()
+      body = botdata.activity_json  # Get current request body as json object
+      activity_id = body[id] # Get the ID from the POST body
+    """
+    def __init__(self):
+        pass
+
+    @property
+    def activity_json(self) -> json:
+        body_text = self.get_request_body()
+        #print(f"ACTIVITY_JSON: Body{body_text}", file=sys.stderr)
+        body = json.loads(body_text) if body_text != None else None
+        return body
+    
+    def get_request_body(self) -> str:
+        if self.detect_flask():
+            flask_app = self.get_flask_app()
+            
+            with flask_app.app_context():
+                mod = __import__('flask', fromlist=['Flask'])
+                request = getattr(mod, 'request')
+                body = self.body_from_WSGI_environ(request.environ)
+            return body
+        else:
+            if self.detect_django():
+                # Retrieve from Middleware cache
+                return retrieve_bot_body()
+
+    def body_from_WSGI_environ(self, environ):
+        try:
+            request_body_size = int(environ.get('CONTENT_LENGTH', 0))
+        except (ValueError):
+            request_body_size = 0
+        request_body = environ['wsgi.input'].read(request_body_size)
+        return request_body
+
+    def detect_flask(self) -> bool:
+        return "flask" in sys.modules
+
+    def detect_django(self) -> bool:
+        return "django" in sys.modules
+
+    def resolve_flask_type(self) -> 'Flask':
+        mod = __import__('flask', fromlist=['Flask'])
+        flask_type = getattr(mod, 'Flask')
+        return flask_type
+
+    def get_flask_app(self) -> 'Flask':
+        flask = [o for o in gc.get_objects() if isinstance(o, self.resolve_flask_type())]
+        flask_instances = len(flask)
+        if flask_instances <= 0 or flask_instances > 1:
+            raise Exception(f'Detected {flask_instances} instances of flask.  Expecting 1.')
+        app = flask[0]
+        return app
\ No newline at end of file
diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/__init__.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/__init__.py
index 017c4f21..cf0c3524 100644
--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/__init__.py
+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/__init__.py
@@ -11,7 +11,6 @@ from .choice_factory_options import ChoiceFactoryOptions
 from .choice_factory import ChoiceFactory
 from .find_choices_options import FindChoicesOptions, FindValuesOptions
 from .found_choice import FoundChoice
-from .found_value import FoundValue
 from .list_style import ListStyle
 from .model_result import ModelResult
 from .sorted_value import SortedValue
diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/find.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/find.py
index ce720739..3bbf4d86 100644
--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/find.py
+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/find.py
@@ -1,15 +1,12 @@
 # Copyright (c) Microsoft Corporation. All rights reserved.
 # Licensed under the MIT License.
 
-from typing import Callable, List, Union
+from typing import List, Union
 
 from .choice import Choice
 from .find_choices_options import FindChoicesOptions, FindValuesOptions
-from .found_value import FoundValue
 from .model_result import ModelResult
 from .sorted_value import SortedValue
-from .token import Token
-from .tokenizer import Tokenizer
 
 class Find:
     """ Contains methods for matching user input against a list of choices """
@@ -41,11 +38,7 @@ class Find:
             if not opt.no_value:
                 synonyms.append( SortedValue(value=choice.value, index=index) )
             
-            if (
-                getattr(choice, 'action', False) and 
-                getattr(choice.action, 'title', False) and 
-                not opt.no_value
-            ):
+            if getattr(choice, 'action', False) and getattr(choice.action, 'title', False) and not opt.no_value:
                 synonyms.append( SortedValue(value=choice.action.title, index=index) )
             
             if choice.synonyms != None:
@@ -71,111 +64,4 @@ class Find:
         # Search for each value within the utterance.
         matches: [ModelResult] = []
         opt = options if options else FindValuesOptions()
-        tokenizer: Callable[[str, str], List[Token]] = opt.tokenizer if opt.tokenizer else Tokenizer.default_tokenizer
-        tokens = tokenizer(utterance, opt.locale)
-        max_distance = opt.max_token_distance if opt.max_token_distance != None else 2
-
-        for i in range(len(sorted_values)):
-            entry = sorted_values[i]
-
-            # Find all matches for a value
-            # - To match "last one" in "the last time I chose the last one" we need
-            #   to re-search the string starting from the end of the previous match.
-            # - The start & end position returned for the match are token positions.
-            start_pos = 0
-            searched_tokens = tokenizer(entry.value.strip(), opt.locale)
-
-            while start_pos < len(tokens):
-                # match = 
-                # write match_value
-                pass
-    
-    @staticmethod
-    def _match_value(
-        source_tokens: List[Token],
-        max_distance: int,
-        options: FindValuesOptions,
-        index: int,
-        value: str,
-        searched_tokens: List[Token],
-        start_pos: int
-    ) -> ModelResult:
-        # Match value to utterance and calculate total deviation.
-        # - The tokens are matched in order so "second last" will match in
-        #   "the second from last one" but not in "the last from the second one".
-        # - The total deviation is a count of the number of tokens skipped in the
-        #   match so for the example above the number of tokens matched would be
-        #   2 and the total deviation would be 1.
-        matched = 0
-        total_deviation = 0
-        start = -1
-        end = -1
-
-        for token in searched_tokens:
-            # Find the position of the token in the utterance.
-            pos = Find._index_of_token(source_tokens, token, start_pos)
-            if (pos >= 0):
-                # Calculate the distance between the current token's position and the previous token's distance.
-                distance = pos - start_pos if matched > 0 else 0
-                if distance <= max_distance:
-                    # Update count of tokens matched and move start pointer to search for next token
-                    # after the current token
-                    matched += 1
-                    total_deviation += distance
-                    start_pos = pos + 1
-
-                    # Update start & end position that will track the span of the utterance that's matched.
-                    if (start < 0):
-                        start = pos
-                    
-                    end = pos
-        
-        # Calculate score and format result
-        # - The start & end positions and the results text field will be corrected by the caller.
-        result: ModelResult = None
-
-        if (
-            matched > 0 and
-            (matched == len(searched_tokens) or options.allow_partial_matches)
-        ):
-            # Percentage of tokens matched. If matching "second last" in
-            # "the second form last one" the completeness would be 1.0 since
-            # all tokens were found.
-            completeness = matched / len(searched_tokens)
-
-            # Accuracy of the match. The accuracy is reduced by additional tokens
-            # occuring in the value that weren't in the utterance. So an utterance
-            # of "second last" matched against a value of "second from last" would
-            # result in an accuracy of 0.5.
-            accuracy = float(matched) / (matched + total_deviation)
-
-            # The final score is simply the compeleteness multiplied by the accuracy.
-            score = completeness * accuracy
-
-            # Format result
-            result = ModelResult(
-                text = 'FILLER - FIND ACTUAL TEXT TO PLACE',
-                start = start,
-                end = end,
-                type_name = "value",
-                resolution = FoundValue(
-                    value = value,
-                    index = index,
-                    score = score
-                )
-            )
-        
-        return result
-    
-    @staticmethod
-    def _index_of_token(
-        tokens: List[Token],
-        token: Token,
-        start_pos: int
-    ) -> int:
-        for i in range(start_pos, len(tokens)):
-            if tokens[i].normalized == token.normalized:
-                return i
-        
-        return -1
-
+        # tokenizer = opt.tokenizer if opt.tokenizer else 
\ No newline at end of file
diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/found_value.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/found_value.py
deleted file mode 100644
index 03409854..00000000
--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/found_value.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-
-class FoundValue:
-    """ Represents a result from matching user input against a list of choices """
-
-    def __init__(
-        self,
-        value: str,
-        index: int,
-        score: float,
-    ):
-        """
-        Parameters:
-        ----------
-
-        value: The value that was matched.
-        
-        index: The index of the value that was matched.
-
-        score: The accuracy with which the synonym matched the specified portion of the utterance.
-        A value of 1.0 would indicate a perfect match.
-
-        """
-        self.value = value
-        self.index = index
-        self.score = score
\ No newline at end of file
diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/tokenizer.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/tokenizer.py
index b920acad..e3e164dd 100644
--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/tokenizer.py
+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/choices/tokenizer.py
@@ -10,16 +10,6 @@ class Tokenizer:
 
     @staticmethod
     def default_tokenizer(text: str, locale: str = None) -> [Token]:
-        """
-        Simple tokenizer that breaks on spaces and punctuation. The only normalization is to lowercase.
-
-        Parameter:
-        ---------
-
-        text: The input text.
-
-        locale: (Optional) Identifies the locale of the input text.
-        """
         tokens: [Token] = []
         token: Union[Token, None] = None
 
@@ -34,69 +24,6 @@ class Tokenizer:
             char = chr(code_point)
 
             # Process current character
-            if Tokenizer._is_breaking_char(code_point):
-                # Character is in Unicode Plane 0 and is in an excluded block
-                Tokenizer._append_token(tokens, token, i - 1)
-                token = None
-            elif code_point > 0xFFFF:
-                # Character is in a Supplementary Unicode Plane. This is where emoji live so
-                # we're going to just break each character in this range out as its own token
-                Tokenizer._append_token(tokens, token, i - 1)
-                token = None
-                tokens.append(Token(
-                    start = i,
-                    end = i + (len(char) - 1),
-                    text = char,
-                    normalized = char
-                ))
-            elif token == None:
-                # Start a new token
-                token = Token(
-                    start = i,
-                    end = 0,
-                    text = char,
-                    normalized = None    
-                )
-            else:
-                # Add onto current token
-                token.text += char
-            
-            i += len(char)
-        
-        Tokenizer._append_token(tokens, token, length)
-        
-        return tokens
-                
-
-    @staticmethod
-    def _is_breaking_char(code_point) -> bool:
-        return (
-            Tokenizer._is_between(code_point, 0x0000, 0x002F) or 
-            Tokenizer._is_between(code_point, 0x003A, 0x0040) or
-            Tokenizer._is_between(code_point, 0x005B, 0x0060) or
-            Tokenizer._is_between(code_point, 0x007B, 0x00BF) or
-            Tokenizer._is_between(code_point, 0x02B9, 0x036F) or
-            Tokenizer._is_between(code_point, 0x2000, 0x2BFF) or
-            Tokenizer._is_between(code_point, 0x2E00, 0x2E7F)
-        )
+            # WRITE IsBreakingChar(code_point)
     
-    @staticmethod
-    def _is_between(value: int, from_val: int, to_val: int) -> bool:
-        """
-        Parameters:
-        -----------
-
-        value: number value
-
-        from: low range
-
-        to: high range
-        """
-        return value >= from_val and value <= to_val
-    
-    @staticmethod
-    def _append_token(tokens: [Token], token: Token, end: int):
-        if (token != None):
-            token.end = end
-            token.normalized = token.text.lower()
-            tokens.append(token)
+    # def _is
\ No newline at end of file
diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_set.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_set.py
index bfc82716..e6d48cca 100644
--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_set.py
+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/dialog_set.py
@@ -1,14 +1,17 @@
 # Copyright (c) Microsoft Corporation. All rights reserved.
 # Licensed under the MIT License.
 import inspect
-from typing import Dict
-
-from botbuilder.core import (TurnContext, BotAssert, StatePropertyAccessor)
 from .dialog import Dialog
 from .dialog_state import DialogState
 from .dialog_turn_result import DialogTurnResult
 from .dialog_reason import DialogReason
 from .dialog_context import DialogContext
+from botbuilder.core import (
+                            TurnContext,
+                            BotAssert,
+                            StatePropertyAccessor
+                            )
+from typing import Dict
 
 
 class DialogSet():
diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/activity_prompt.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/activity_prompt.py
index d4b39793..2d0e0cb1 100644
--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/activity_prompt.py
+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/activity_prompt.py
@@ -8,6 +8,7 @@ from botbuilder.core import TurnContext
 from botbuilder.dialogs import Dialog, DialogContext, DialogInstance, DialogReason, DialogTurnResult
 from botbuilder.schema import Activity, ActivityTypes, InputHints
 
+from .prompt import Prompt
 from .prompt_options import PromptOptions
 from .prompt_recognizer_result import PromptRecognizerResult
 from .prompt_validator_context import PromptValidatorContext
@@ -36,6 +37,8 @@ class ActivityPrompt(Dialog, ABC):
         """
         Dialog.__init__(self, dialog_id)
         
+        if validator is None:
+            raise TypeError('validator was expected but received None')
         self._validator = validator
 
     async def begin_dialog(self, dc: DialogContext, options: PromptOptions) -> DialogTurnResult:
@@ -45,16 +48,18 @@ class ActivityPrompt(Dialog, ABC):
             raise TypeError('ActivityPrompt.begin_dialog(): Prompt options are required for ActivityPrompts.')
         
         # Ensure prompts have input hint set
-        if options.prompt != None and not options.prompt.input_hint:
+        if options.prompt is not None and not options.prompt.input_hint:
             options.prompt.input_hint = InputHints.expecting_input
 
-        if options.retry_prompt != None and not options.retry_prompt.input_hint:
+        if options.retry_prompt is not None and not options.retry_prompt.input_hint:
             options.retry_prompt.input_hint = InputHints.expecting_input
 
         # Initialize prompt state
         state: Dict[str, object] = dc.active_dialog.state
         state[self.persisted_options] = options
-        state[self.persisted_state] = Dict[str, object]
+        state[self.persisted_state] = {
+            Prompt.ATTEMPT_COUNT_KEY: 0
+        }
 
         # Send initial prompt
         await self.on_prompt(
@@ -76,9 +81,12 @@ class ActivityPrompt(Dialog, ABC):
         options: Dict[str, object] = instance.state[self.persisted_options]
         recognized: PromptRecognizerResult = await self.on_recognize(dc.context, state, options)
 
+        # Increment attempt count
+        state[Prompt.ATTEMPT_COUNT_KEY] += 1
+
         # Validate the return value
         is_valid = False
-        if self._validator != None:
+        if self._validator is not None:
             prompt_context = PromptValidatorContext(
                 dc.context,
                 recognized,
@@ -125,7 +133,7 @@ class ActivityPrompt(Dialog, ABC):
         context: TurnContext, 
         state: Dict[str, dict], 
         options: PromptOptions,
-        isRetry: bool = False
+        is_retry: bool = False
     ):
         """
         Called anytime the derived class should send the user a prompt.
@@ -140,11 +148,11 @@ class ActivityPrompt(Dialog, ABC):
 
         isRetry: If `true` the users response wasn't recognized and the re-prompt should be sent.
         """
-        if isRetry and options.retry_prompt:
+        if is_retry and options.retry_prompt:
             options.retry_prompt.input_hint = InputHints.expecting_input
             await context.send_activity(options.retry_prompt)
         elif options.prompt:
-            options.prompt = InputHints.expecting_input
+            options.prompt.input_hint = InputHints.expecting_input
             await context.send_activity(options.prompt)
     
     async def on_recognize(
diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/prompt.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/prompt.py
index aea0c99f..30524102 100644
--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/prompt.py
+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/prompt.py
@@ -20,6 +20,7 @@ from botbuilder.schema import Activity
 """ Base class for all prompts.
 """
 class Prompt(Dialog):
+    ATTEMPT_COUNT_KEY = "AttemptCount"
     persisted_options = "options"
     persisted_state = "state"
     def __init__(self, dialog_id: str, validator: object = None):
@@ -52,7 +53,7 @@ class Prompt(Dialog):
         # Initialize prompt state
         state = dc.active_dialog.state
         state[self.persisted_options] = options
-        state[self.persisted_state] = Dict[str, object]
+        state[self.persisted_state] = {}
 
         # Send initial prompt
         await self.on_prompt(dc.context, state[self.persisted_state], state[self.persisted_options], False)
diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/prompt_validator_context.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/prompt_validator_context.py
index 1a0fc6dd..422f3de6 100644
--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/prompt_validator_context.py
+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/prompts/prompt_validator_context.py
@@ -6,8 +6,6 @@ from .prompt_options import PromptOptions
 from .prompt_recognizer_result import PromptRecognizerResult
 
 
-""" Contextual information passed to a custom `PromptValidator`.
-"""
 class PromptValidatorContext():
     def __init__(self, turn_context: TurnContext, recognized: PromptRecognizerResult, state: Dict[str, object], options: PromptOptions): 
         """Creates contextual information passed to a custom `PromptValidator`.
@@ -71,3 +69,11 @@ class PromptValidatorContext():
         The validator can extend this interface to support additional prompt options.
         """
         return self._options
+
+    @property
+    def attempt_count(self) -> int:
+        """
+        Gets the number of times the prompt has been executed.
+        """
+        from botbuilder.dialogs.prompts import Prompt
+        return self._state.get(Prompt.ATTEMPT_COUNT_KEY, 0)
diff --git a/libraries/botbuilder-dialogs/botbuilder/dialogs/waterfall_dialog.py b/libraries/botbuilder-dialogs/botbuilder/dialogs/waterfall_dialog.py
index 94f84ae6..cd833538 100644
--- a/libraries/botbuilder-dialogs/botbuilder/dialogs/waterfall_dialog.py
+++ b/libraries/botbuilder-dialogs/botbuilder/dialogs/waterfall_dialog.py
@@ -1,151 +1,150 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-
-
-import uuid
-from typing import ( 
-                    Dict, 
-                    Coroutine, 
-                    List)
-from .dialog_reason import DialogReason
-from .dialog import Dialog
-from .dialog_turn_result import DialogTurnResult
-from .dialog_context import DialogContext
-from .dialog_instance import DialogInstance
-from .waterfall_step_context import WaterfallStepContext
-from botbuilder.core import TurnContext
-from botbuilder.schema import ActivityTypes
-from typing import Coroutine, List
-
-
-class WaterfallDialog(Dialog):
-    PersistedOptions = "options"
-    StepIndex = "stepIndex"
-    PersistedValues = "values"
-    PersistedInstanceId = "instanceId"
-    
-    def __init__(self, dialog_id: str, steps: [Coroutine] = None):
-        super(WaterfallDialog, self).__init__(dialog_id)
-        if not steps:
-            self._steps = []
-        else:
-            if not isinstance(steps, list):
-                raise TypeError('WaterfallDialog(): steps must be list of steps')
-            self._steps = steps
-
-    def add_step(self, step):
-        """
-        Adds a new step to the waterfall.
-        :param step: Step to add
-           
-        :return: Waterfall dialog for fluent calls to `add_step()`.
-        """
-        if not step:
-            raise TypeError('WaterfallDialog.add_step(): step cannot be None.')
-
-        self._steps.append(step)
-        return self
-    
-    async def begin_dialog(self, dc: DialogContext, options: object = None) -> DialogTurnResult:
-        
-        if not dc:
-            raise TypeError('WaterfallDialog.begin_dialog(): dc cannot be None.')
-        
-        # Initialize waterfall state
-        state = dc.active_dialog.state
-        
-        instance_id = uuid.uuid1().__str__()
-        state[self.PersistedOptions] = options
-        state[self.PersistedValues] = {}
-        state[self.PersistedInstanceId] = instance_id
-        
-        properties = {}
-        properties['DialogId'] = self.id
-        properties['InstanceId'] = instance_id
-        self.telemetry_client.track_event("WaterfallStart", properties = properties)
-
-        # Run first stepkinds
-        return await self.run_step(dc, 0, DialogReason.BeginCalled, None) 
-    
-    async def continue_dialog(self, dc: DialogContext = None, reason: DialogReason = None, result: object = NotImplementedError) -> DialogTurnResult:
-        if not dc:
-            raise TypeError('WaterfallDialog.continue_dialog(): dc cannot be None.')
-        
-        if dc.context.activity.type != ActivityTypes.message:
-            return Dialog.end_of_turn
-        
-        return await self.resume_dialog(dc, DialogReason.ContinueCalled, dc.context.activity.text)
-    
-    async def resume_dialog(self, dc: DialogContext, reason: DialogReason, result: object):
-        if dc is None:
-            raise TypeError('WaterfallDialog.resume_dialog(): dc cannot be None.')
-        
-        # Increment step index and run step
-        state = dc.active_dialog.state
-        
-        # Future Me: 
-        # If issues with CosmosDB, see https://github.com/Microsoft/botbuilder-dotnet/issues/871
-        # for hints.
-        return await self.run_step(dc, state[self.StepIndex] + 1, reason, result)
-    
-    async def end_dialog(self, turn_context: TurnContext, instance: DialogInstance, reason: DialogReason) -> None:
-        if reason is DialogReason.CancelCalled:
-            index = instance.state[self.StepIndex]
-            step_name = self.get_step_name(index)
-            instance_id = str(instance.state[self.PersistedInstanceId])
-            properties = {
-                "DialogId": self.id, 
-                "StepName" : step_name,
-                "InstanceId" : instance_id 
-            }
-            self.telemetry_client.track_event("WaterfallCancel", properties)
-        else:
-            if reason is DialogReason.EndCalled:
-
-                instance_id = str(instance.state[self.PersistedInstanceId])
-                properties = {
-                    "DialogId": self.id,
-                    "InstanceId": instance_id
-                }
-                self.telemetry_client.track_event("WaterfallComplete", properties)
-        
-        return 
-    
-    async def on_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
-        step_name = self.get_step_name(step_context.index)
-        instance_id = str(step_context.active_dialog.state[self.PersistedInstanceId])
-        properties = {
-                "DialogId": self.id,
-                "StepName": step_name,
-                "InstanceId": instance_id 
-            }
-        self.telemetry_client.track_event("WaterfallStep", properties)
-        return await self._steps[step_context.index](step_context)
-    
-    async def run_step(self, dc: DialogContext, index: int, reason: DialogReason, result: object) -> DialogTurnResult:
-        if not dc:
-            raise TypeError('WaterfallDialog.run_steps(): dc cannot be None.')
-        if index < len(self._steps):
-            # Update persisted step index
-            state = dc.active_dialog.state
-            state[self.StepIndex] = index
-            
-            # Create step context
-            options = state[self.PersistedOptions]
-            values = state[self.PersistedValues]
-            step_context = WaterfallStepContext(self, dc, options, values, index, reason, result)
-            return await self.on_step(step_context)
-        else:
-            # End of waterfall so just return any result to parent
-            return await dc.end_dialog(result)
-
-    def get_step_name(self, index: int) -> str:
-        """
-        Give the waterfall step a unique name
-        """
-        step_name = self._steps[index].__qualname__
-
-        if not step_name or ">" in step_name :
-            step_name = f"Step{index + 1}of{len(self._steps)}"
-
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+
+
+import uuid
+from typing import ( 
+                    Dict, 
+                    Coroutine, 
+                    List)
+from .dialog_reason import DialogReason
+from .dialog import Dialog
+from .dialog_turn_result import DialogTurnResult
+from .dialog_context import DialogContext
+from .dialog_instance import DialogInstance
+from .waterfall_step_context import WaterfallStepContext
+from botbuilder.core import TurnContext
+from botbuilder.schema import ActivityTypes
+from typing import Coroutine, List
+
+
+class WaterfallDialog(Dialog):
+    PersistedOptions = "options"
+    StepIndex = "stepIndex"
+    PersistedValues = "values"
+    PersistedInstanceId = "instanceId"
+    
+    def __init__(self, dialog_id: str, steps: [Coroutine] = None):
+        super(WaterfallDialog, self).__init__(dialog_id)
+        if not steps:
+            self._steps = []
+        else:
+            if not isinstance(steps, list):
+                raise TypeError('WaterfallDialog(): steps must be list of steps')
+            self._steps = steps
+
+    def add_step(self, step):
+        """
+        Adds a new step to the waterfall.
+        :param step: Step to add
+           
+        :return: Waterfall dialog for fluent calls to `add_step()`.
+        """
+        if not step:
+            raise TypeError('WaterfallDialog.add_step(): step cannot be None.')
+
+        self._steps.append(step)
+        return self
+    
+    async def begin_dialog(self, dc: DialogContext, options: object = None) -> DialogTurnResult:
+        
+        if not dc:
+            raise TypeError('WaterfallDialog.begin_dialog(): dc cannot be None.')
+        
+        # Initialize waterfall state
+        state = dc.active_dialog.state
+        
+        instance_id = uuid.uuid1().__str__()
+        state[self.PersistedOptions] = options
+        state[self.PersistedValues] = {}
+        state[self.PersistedInstanceId] = instance_id
+        
+        properties = {}
+        properties['DialogId'] = self.id
+        properties['InstanceId'] = instance_id
+        self.telemetry_client.track_event("WaterfallStart", properties)
+        # Run first stepkinds
+        return await self.run_step(dc, 0, DialogReason.BeginCalled, None) 
+    
+    async def continue_dialog(self, dc: DialogContext = None, reason: DialogReason = None, result: object = NotImplementedError) -> DialogTurnResult:
+        if not dc:
+            raise TypeError('WaterfallDialog.continue_dialog(): dc cannot be None.')
+        
+        if dc.context.activity.type != ActivityTypes.message:
+            return Dialog.end_of_turn
+        
+        return await self.resume_dialog(dc, DialogReason.ContinueCalled, dc.context.activity.text)
+    
+    async def resume_dialog(self, dc: DialogContext, reason: DialogReason, result: object):
+        if dc is None:
+            raise TypeError('WaterfallDialog.resume_dialog(): dc cannot be None.')
+        
+        # Increment step index and run step
+        state = dc.active_dialog.state
+        
+        # Future Me: 
+        # If issues with CosmosDB, see https://github.com/Microsoft/botbuilder-dotnet/issues/871
+        # for hints.
+        return await self.run_step(dc, state[self.StepIndex] + 1, reason, result)
+    
+    async def end_dialog(self, turn_context: TurnContext, instance: DialogInstance, reason: DialogReason) -> None:
+        if reason is DialogReason.CancelCalled:
+            index = instance.state[self.StepIndex]
+            step_name = self.get_step_name(index)
+            instance_id = str(instance.state[self.PersistedInstanceId])
+            properties = {
+                "DialogId": self.id, 
+                "StepName" : step_name,
+                "InstanceId" : instance_id 
+            }
+            self.telemetry_client.track_event("WaterfallCancel", properties)
+        else:
+            if reason is DialogReason.EndCalled:
+
+                instance_id = str(instance.state[self.PersistedInstanceId])
+                properties = {
+                    "DialogId": self.id,
+                    "InstanceId": instance_id
+                }
+                self.telemetry_client.track_event("WaterfallComplete", properties)
+        
+        return 
+    
+    async def on_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
+        step_name = self.get_step_name(step_context.index)
+        instance_id = str(step_context.active_dialog.state[self.PersistedInstanceId])
+        properties = {
+                "DialogId": self.id,
+                "StepName": step_name,
+                "InstanceId": instance_id 
+            }
+        self.telemetry_client.track_event("WaterfallStep", properties)
+        return await self._steps[step_context.index](step_context)
+    
+    async def run_step(self, dc: DialogContext, index: int, reason: DialogReason, result: object) -> DialogTurnResult:
+        if not dc:
+            raise TypeError('WaterfallDialog.run_steps(): dc cannot be None.')
+        if index < len(self._steps):
+            # Update persisted step index
+            state = dc.active_dialog.state
+            state[self.StepIndex] = index
+            
+            # Create step context
+            options = state[self.PersistedOptions]
+            values = state[self.PersistedValues]
+            step_context = WaterfallStepContext(self, dc, options, values, index, reason, result)
+            return await self.on_step(step_context)
+        else:
+            # End of waterfall so just return any result to parent
+            return await dc.end_dialog(result)
+
+    def get_step_name(self, index: int) -> str:
+        """
+        Give the waterfall step a unique name
+        """
+        step_name = self._steps[index].__qualname__
+
+        if not step_name or ">" in step_name :
+            step_name = f"Step{index + 1}of{len(self._steps)}"
+
         return step_name
\ No newline at end of file
diff --git a/samples/python-flask/13.core-bot/app.py b/samples/python-flask/13.core-bot/app.py
index f634f7fd..c166d45f 100644
--- a/samples/python-flask/13.core-bot/app.py
+++ b/samples/python-flask/13.core-bot/app.py
@@ -46,8 +46,7 @@ def messages():
     auth_header = request.headers['Authorization'] if 'Authorization' in request.headers else ''
 
     async def aux_func(turn_context):
-        await BOT.on_turn(turn_context)
-
+        asyncio.ensure_future(BOT.on_turn(turn_context))
     try:
         task = LOOP.create_task(ADAPTER.process_activity(activity, auth_header, aux_func))
         LOOP.run_until_complete(task)
diff --git a/samples/python-flask/21.corebot-app-insights/README-LUIS.md b/samples/python-flask/21.corebot-app-insights/README-LUIS.md
deleted file mode 100644
index 61bde771..00000000
--- a/samples/python-flask/21.corebot-app-insights/README-LUIS.md
+++ /dev/null
@@ -1,216 +0,0 @@
-# Setting up LUIS via CLI:
-
-This README contains information on how to create and deploy a LUIS application. When the bot is ready to be deployed to production, we recommend creating a LUIS Endpoint Resource for usage with your LUIS App.
-
-> _For instructions on how to create a LUIS Application via the LUIS portal, see these Quickstart steps:_
-> 1. _[Quickstart: Create a new app in the LUIS portal][Quickstart-create]_
-> 2. _[Quickstart: Deploy an app in the LUIS portal][Quickstart-deploy]_
-
-  [Quickstart-create]: https://docs.microsoft.com/azure/cognitive-services/luis/get-started-portal-build-app
-  [Quickstart-deploy]:https://docs.microsoft.com/azure/cognitive-services/luis/get-started-portal-deploy-app
-
-## Table of Contents:
-
-- [Prerequisites](#Prerequisites)
-- [Import a new LUIS Application using a local LUIS application](#Import-a-new-LUIS-Application-using-a-local-LUIS-application)
-- [How to create a LUIS Endpoint resource in Azure and pair it with a LUIS Application](#How-to-create-a-LUIS-Endpoint-resource-in-Azure-and-pair-it-with-a-LUIS-Application)
-
-___
-
-## [Prerequisites](#Table-of-Contents):
-
-#### Install Azure CLI >=2.0.61:
-
-Visit the following page to find the correct installer for your OS:
-- https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest
-
-#### Install LUIS CLI >=2.4.0:
-
-Open a CLI of your choice and type the following:
-
-```bash
-npm i -g luis-apis@^2.4.0
-```
-
-#### LUIS portal account:
-
-You should already have a LUIS account with either https://luis.ai, https://eu.luis.ai, or https://au.luis.ai. To determine where to create a LUIS account, consider where you will deploy your LUIS applications, and then place them in [the corresponding region][LUIS-Authoring-Regions].
-
-After you've created your account, you need your [Authoring Key][LUIS-AKey] and a LUIS application ID.
-
-  [LUIS-Authoring-Regions]: https://docs.microsoft.com/azure/cognitive-services/luis/luis-reference-regions#luis-authoring-regions]
-  [LUIS-AKey]: https://docs.microsoft.com/azure/cognitive-services/luis/luis-concept-keys#authoring-key
-
-___
-
-## [Import a new LUIS Application using a local LUIS application](#Table-of-Contents)
-
-### 1. Import the local LUIS application to luis.ai
-
-```bash
-luis import application --region "LuisAppAuthoringRegion" --authoringKey "LuisAuthoringKey" --appName "FlightBooking" --in "./cognitiveModels/FlightBooking.json"
-```
-
-Outputs the following JSON:
-
-```json
-{
-    "id": "########-####-####-####-############",
-    "name": "FlightBooking",
-    "description": "A LUIS model that uses intent and entities.",
-    "culture": "en-us",
-    "usageScenario": "",
-    "domain": "",
-    "versionsCount": 1,
-    "createdDateTime": "2019-03-29T18:32:02Z",
-    "endpoints": {},
-    "endpointHitsCount": 0,
-    "activeVersion": "0.1",
-    "ownerEmail": "bot@contoso.com",
-    "tokenizerVersion": "1.0.0"
-}
-```
-
-For the next step, you'll need the `"id"` value for `--appId` and the `"activeVersion"` value for `--versionId`.
-
-### 2. Train the LUIS Application
-
-```bash
-luis train version --region "LuisAppAuthoringRegion" --authoringKey "LuisAuthoringKey" --appId "LuisAppId" --versionId "LuisAppversion" --wait
-```
-
-### 3. Publish the LUIS Application
-
-```bash
-luis publish version --region "LuisAppAuthoringRegion" --authoringKey "LuisAuthoringKey" --appId "LuisAppId" --versionId "LuisAppversion" --publishRegion "LuisAppPublishRegion"
-```
-
-> `--region` corresponds to the region you _author_ your application in. The regions available for this are "westus", "westeurope" and "australiaeast". <br/>
-> These regions correspond to the three available portals, https://luis.ai, https://eu.luis.ai, or https://au.luis.ai. <br/>
-> `--publishRegion` corresponds to the region of the endpoint you're publishing to, (e.g. "westus", "southeastasia", "westeurope", "brazilsouth"). <br/>
-> See the [reference docs][Endpoint-API] for a list of available publish/endpoint regions.
-
-  [Endpoint-API]: https://westus.dev.cognitive.microsoft.com/docs/services/5819c76f40a6350ce09de1ac/operations/5819c77140a63516d81aee78
-
-Outputs the following:
-
-```json
- {
-    "versionId": "0.1",
-    "isStaging": false,
-    "endpointUrl": "https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/########-####-####-####-############",
-    "region": "westus",
-    "assignedEndpointKey": null,
-    "endpointRegion": "westus",
-    "failedRegions": "",
-    "publishedDateTime": "2019-03-29T18:40:32Z",
-    "directVersionPublish": false
-}
-```
-
-To see how to create an LUIS Cognitive Service Resource in Azure, please see [the next README][README-LUIS]. This Resource should be used when you want to move your bot to production. The instructions will show you how to create and pair the resource with a LUIS Application.
-
-  [README-LUIS]: ./README-LUIS.md
-
-___
-
-## [How to create a LUIS Endpoint resource in Azure and pair it with a LUIS Application](#Table-of-Contents)
-
-### 1. Create a new LUIS Cognitive Services resource on Azure via Azure CLI
-
-> _Note:_ <br/>
-> _If you don't have a Resource Group in your Azure subscription, you can create one through the Azure portal or through using:_
-> ```bash
-> az group create --subscription "AzureSubscriptionGuid" --location "westus" --name "ResourceGroupName"
-> ```
-> _To see a list of valid locations, use `az account list-locations`_
-
-
-```bash
-# Use Azure CLI to create the LUIS Key resource on Azure
-az cognitiveservices account create --kind "luis" --name "NewLuisResourceName" --sku "S0" --location "westus" --subscription "AzureSubscriptionGuid" -g "ResourceGroupName"
-```
-
-The command will output a response similar to the JSON below:
-
-```json
-{
-  "endpoint": "https://westus.api.cognitive.microsoft.com/luis/v2.0",
-  "etag": "\"########-####-####-####-############\"",
-  "id": "/subscriptions/########-####-####-####-############/resourceGroups/ResourceGroupName/providers/Microsoft.CognitiveServices/accounts/NewLuisResourceName",
-  "internalId": "################################",
-  "kind": "luis",
-  "location": "westus",
-  "name": "NewLuisResourceName",
-  "provisioningState": "Succeeded",
-  "resourceGroup": "ResourceGroupName",
-  "sku": {
-    "name": "S0",
-    "tier": null
-  },
-  "tags": null,
-  "type": "Microsoft.CognitiveServices/accounts"
-}
-```
-
-
-
-Take the output from the previous command and create a JSON file in the following format:
-
-```json
-{
-    "azureSubscriptionId": "00000000-0000-0000-0000-000000000000",
-    "resourceGroup": "ResourceGroupName",
-    "accountName": "NewLuisResourceName"
-}
-```
-
-### 2. Retrieve ARM access token via Azure CLI
-
-```bash
-az account get-access-token --subscription "AzureSubscriptionGuid"
-```
-
-This will return an object that looks like this:
-
-```json
-{
-  "accessToken": "eyJ0eXAiOiJKVtokentokentokentokentokeng1dCI6Ik4tbEMwbi05REFMcXdodUhZbkhRNjNHZUNYYyIsItokenI6Ik4tbEMwbi05REFMcXdodUhZbkhRNjNHZUNYYyJ9.eyJhdWQiOiJodHRwczovL21hbmFnZW1lbnQuY29yZS53aW5kb3dzLm5ldC8iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83MmY5ODhiZi04NmYxLTQxYWYtOTFhYi0yZDdjZDAxMWRiNDcvIiwiaWF0IjoxNTUzODc3MTUwLCJuYmYiOjE1NTM4NzcxNTAsImV4cCI6MTU1Mzg4MTA1MCwiX2NsYWltX25hbWVzIjp7Imdyb3VwcyI6InNyYzEifSwiX2NsYWltX3NvdXJjZXMiOnsic3JjMSI6eyJlbmRwb2ludCI6Imh0dHBzOi8vZ3JhcGgud2luZG93cy5uZXQvNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjQ3L3VzZXJzL2ZmZTQyM2RkLWJhM2YtNDg0Ny04NjgyLWExNTI5MDA4MjM4Ny9nZXRNZW1iZXJPYmplY3RzIn19LCJhY3IiOiIxIiwiYWlvIjoiQVZRQXEvOEtBQUFBeGVUc201NDlhVHg4RE1mMFlRVnhGZmxxOE9RSC9PODR3QktuSmRqV1FqTkkwbmxLYzB0bHJEZzMyMFZ5bWZGaVVBSFBvNUFFUTNHL0FZNDRjdk01T3M0SEt0OVJkcE5JZW9WU0dzd0kvSkk9IiwiYW1yIjpbIndpYSIsIm1mYSJdLCJhcHBpZCI6IjA0YjA3Nzk1LThkZGItNDYxYS1iYmVlLTAyZjllMWJmN2I0NiIsImFwcGlkYWNyIjoiMCIsImRldmljZWlkIjoiNDhmNDVjNjEtMTg3Zi00MjUxLTlmZWItMTllZGFkZmMwMmE3IiwiZmFtaWx5X25hbWUiOiJHdW0iLCJnaXZlbl9uYW1lIjoiU3RldmVuIiwiaXBhZGRyIjoiMTY3LjIyMC4yLjU1IiwibmFtZSI6IlN0ZXZlbiBHdW0iLCJvaWQiOiJmZmU0MjNkZC1iYTNmLTQ4NDctODY4Mi1hMTUyOTAwODIzODciLCJvbnByZW1fc2lkIjoiUy0xLTUtMjEtMjEyNzUyMTE4NC0xNjA0MDEyOTIwLTE4ODc5Mjc1MjctMjYwOTgyODUiLCJwdWlkIjoiMTAwMzdGRkVBMDQ4NjlBNyIsInJoIjoiSSIsInNjcCI6InVzZXJfaW1wZXJzb25hdGlvbiIsInN1YiI6Ik1rMGRNMWszN0U5ckJyMjhieUhZYjZLSU85LXVFQVVkZFVhNWpkSUd1Nk0iLCJ0aWQiOiI3MmY5ODhiZi04NmYxLTQxYWYtOTFhYi0yZDdjZDAxMWRiNDciLCJ1bmlxdWVfbmFtZSI6InN0Z3VtQG1pY3Jvc29mdC5jb20iLCJ1cG4iOiJzdGd1bUBtaWNyb3NvZnQuY29tIiwidXRpIjoiT2w2NGN0TXY4RVNEQzZZQWRqRUFtokenInZlciI6IjEuMCJ9.kFAsEilE0mlS1pcpqxf4rEnRKeYsehyk-gz-zJHUrE__oad3QjgDSBDPrR_ikLdweynxbj86pgG4QFaHURNCeE6SzrbaIrNKw-n9jrEtokenlosOxg_0l2g1LeEUOi5Q4gQREAU_zvSbl-RY6sAadpOgNHtGvz3Rc6FZRITfkckSLmsKAOFoh-aWC6tFKG8P52rtB0qVVRz9tovBeNqkMYL49s9ypduygbXNVwSQhm5JszeWDgrFuVFHBUP_iENCQYGQpEZf_KvjmX1Ur1F9Eh9nb4yI2gFlKncKNsQl-tokenK7-tokentokentokentokentokentokenatoken",
-  "expiresOn": "2200-12-31 23:59:59.999999",
-  "subscription": "AzureSubscriptionGuid",
-  "tenant": "tenant-guid",
-  "tokenType": "Bearer"
-}
-```
-
-The value needed for the next step is the `"accessToken"`.
-
-### 3. Use `luis add appazureaccount` to pair your LUIS resource with a LUIS Application
-
-```bash
-luis add appazureaccount --in "path/to/created/requestBody.json" --appId "LuisAppId" --authoringKey "LuisAuthoringKey" --armToken "accessToken"
-```
-
-If successful, it should yield a response like this:
-
-```json
-{
-  "code": "Success",
-  "message": "Operation Successful"
-}
-```
-
-### 4. See the LUIS Cognitive Services' keys
-
-```bash
-az cognitiveservices account keys list --name "NewLuisResourceName" --subscription "AzureSubscriptionGuid" -g "ResourceGroupName"
-```
-
-This will return an object that looks like this:
-
-```json
-{
-  "key1": "9a69####dc8f####8eb4####399f####",
-  "key2": "####f99e####4b1a####fb3b####6b9f"
-}
-```
\ No newline at end of file
diff --git a/samples/python-flask/21.corebot-app-insights/README.md b/samples/python-flask/21.corebot-app-insights/README.md
deleted file mode 100644
index 175429c0..00000000
--- a/samples/python-flask/21.corebot-app-insights/README.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# CoreBot
-
-Bot Framework v4 core bot sample.
-
-This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to:
-
-- Use [LUIS](https://www.luis.ai) to implement core AI capabilities
-- Implement a multi-turn conversation using Dialogs
-- Handle user interruptions for such things as `Help` or `Cancel`
-- Prompt for and validate requests for information from the user
-
-## Prerequisites
-
-This sample **requires** prerequisites in order to run.
-
-### Overview
-
-This bot uses [LUIS](https://www.luis.ai), an AI based cognitive service, to implement language understanding.
-
-### Install Python 3.6
-
-
-### Create a LUIS Application to enable language understanding
-
-LUIS language model setup, training, and application configuration steps can be found [here](https://docs.microsoft.com/azure/bot-service/bot-builder-howto-v4-luis?view=azure-bot-service-4.0&tabs=cs).
-
-If you wish to create a LUIS application via the CLI, these steps can be found in the [README-LUIS.md](README-LUIS.md).
-
-
-## Testing the bot using Bot Framework Emulator
-
-[Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel.
-
-- Install the Bot Framework Emulator version 4.3.0 or greater from [here](https://github.com/Microsoft/BotFramework-Emulator/releases)
-
-### Connect to the bot using Bot Framework Emulator
-
-- Launch Bot Framework Emulator
-- File -> Open Bot
-- Enter a Bot URL of `http://localhost:3978/api/messages`
-
-
-## Further reading
-
-- [Bot Framework Documentation](https://docs.botframework.com)
-- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
-- [Dialogs](https://docs.microsoft.com/azure/bot-service/bot-builder-concept-dialog?view=azure-bot-service-4.0)
-- [Gathering Input Using Prompts](https://docs.microsoft.com/azure/bot-service/bot-builder-prompts?view=azure-bot-service-4.0&tabs=csharp)
-- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)
-- [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0)
-- [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0)
-- [.NET Core CLI tools](https://docs.microsoft.com/dotnet/core/tools/?tabs=netcore2x)
-- [Azure CLI](https://docs.microsoft.com/cli/azure/?view=azure-cli-latest)
-- [Azure Portal](https://portal.azure.com)
-- [Language Understanding using LUIS](https://docs.microsoft.com/azure/cognitive-services/luis/)
-- [Channels and Bot Connector Service](https://docs.microsoft.com/azure/bot-service/bot-concepts?view=azure-bot-service-4.0)
\ No newline at end of file
diff --git a/samples/python-flask/21.corebot-app-insights/booking_details.py b/samples/python-flask/21.corebot-app-insights/booking_details.py
deleted file mode 100644
index 21960cb5..00000000
--- a/samples/python-flask/21.corebot-app-insights/booking_details.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Booking detail."""
-class BookingDetails:
-    """Booking detail implementation"""
-    def __init__(self, destination: str = None, origin: str = None, travel_date: str = None):
-        self.destination = destination
-        self.origin = origin
-        self.travel_date = travel_date
diff --git a/samples/python-flask/21.corebot-app-insights/bots/__init__.py b/samples/python-flask/21.corebot-app-insights/bots/__init__.py
deleted file mode 100644
index b721a932..00000000
--- a/samples/python-flask/21.corebot-app-insights/bots/__init__.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""bots module."""
-
-from .dialog_bot import DialogBot
-from .dialog_and_welcome_bot import DialogAndWelcomeBot
-
-__all__ = [
-    'DialogBot',
-    'DialogAndWelcomeBot']
diff --git a/samples/python-flask/21.corebot-app-insights/bots/dialog_and_welcome_bot.py b/samples/python-flask/21.corebot-app-insights/bots/dialog_and_welcome_bot.py
deleted file mode 100644
index eeb7e42b..00000000
--- a/samples/python-flask/21.corebot-app-insights/bots/dialog_and_welcome_bot.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Main dialog to welcome users."""
-import json
-import os.path
-
-from typing import List
-from botbuilder.dialogs import Dialog
-from botbuilder.core import TurnContext, ConversationState, UserState, BotTelemetryClient
-from botbuilder.schema import Activity, Attachment, ChannelAccount
-from helpers.activity_helper import create_activity_reply
-from .dialog_bot import DialogBot
-
-class DialogAndWelcomeBot(DialogBot):
-    """Main dialog to welcome users."""
-    def __init__(self, conversation_state: ConversationState, user_state: UserState,
-                 dialog: Dialog, telemetry_client: BotTelemetryClient):
-        super(DialogAndWelcomeBot, self).__init__(conversation_state, user_state,
-                                                  dialog, telemetry_client)
-        self.telemetry_client = telemetry_client
-
-    async def on_members_added_activity(self, members_added: List[ChannelAccount],
-                                        turn_context: TurnContext):
-        for member in members_added:
-            # Greet anyone that was not the target (recipient) of this message.
-            # To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards
-            # for more details.
-            if member.id != turn_context.activity.recipient.id:
-                welcome_card = self.create_adaptive_card_attachment()
-                response = self.create_response(turn_context.activity, welcome_card)
-                await turn_context.send_activity(response)
-
-    def create_response(self, activity: Activity, attachment: Attachment):
-        """Create an attachment message response."""
-        response = create_activity_reply(activity)
-        response.attachments = [attachment]
-        return response
-
-    # Load attachment from file.
-    def create_adaptive_card_attachment(self):
-        """Create an adaptive card."""
-        relative_path = os.path.abspath(os.path.dirname(__file__))
-        path = os.path.join(relative_path, "resources/welcomeCard.json")
-        with open(path) as card_file:
-            card = json.load(card_file)
-
-        return Attachment(
-            content_type="application/vnd.microsoft.card.adaptive",
-            content=card)
diff --git a/samples/python-flask/21.corebot-app-insights/bots/dialog_bot.py b/samples/python-flask/21.corebot-app-insights/bots/dialog_bot.py
deleted file mode 100644
index 522f897f..00000000
--- a/samples/python-flask/21.corebot-app-insights/bots/dialog_bot.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Implements bot Activity handler."""
-
-from botbuilder.core import ActivityHandler, ConversationState, UserState, TurnContext, \
-    BotTelemetryClient, NullTelemetryClient
-from botbuilder.dialogs import Dialog
-from helpers.dialog_helper import DialogHelper
-
-class DialogBot(ActivityHandler):
-    """Main activity handler for the bot."""
-    def __init__(self, conversation_state: ConversationState, user_state: UserState,
-                 dialog: Dialog, telemetry_client: BotTelemetryClient):
-        if conversation_state is None:
-            raise Exception('[DialogBot]: Missing parameter. conversation_state is required')
-        if user_state is None:
-            raise Exception('[DialogBot]: Missing parameter. user_state is required')
-        if dialog is None:
-            raise Exception('[DialogBot]: Missing parameter. dialog is required')
-
-        self.conversation_state = conversation_state
-        self.user_state = user_state
-        self.dialog = dialog
-        self.dialogState = self.conversation_state.create_property('DialogState') # pylint:disable=invalid-name
-        self.telemetry_client = telemetry_client
-
-    async def on_turn(self, turn_context: TurnContext):
-        await super().on_turn(turn_context)
-
-        # Save any state changes that might have occured during the turn.
-        await self.conversation_state.save_changes(turn_context, False)
-        await self.user_state.save_changes(turn_context, False)
-
-    async def on_message_activity(self, turn_context: TurnContext):
-        # pylint:disable=invalid-name
-        await DialogHelper.run_dialog(self.dialog, turn_context,
-                                      self.conversation_state.create_property("DialogState"))
-
-    @property
-    def telemetry_client(self) -> BotTelemetryClient:
-        """
-        Gets the telemetry client for logging events.
-        """
-        return self._telemetry_client
-
-    # pylint:disable=attribute-defined-outside-init
-    @telemetry_client.setter
-    def telemetry_client(self, value: BotTelemetryClient) -> None:
-        """
-        Sets the telemetry client for logging events.
-        """
-        if value is None:
-            self._telemetry_client = NullTelemetryClient()
-        else:
-            self._telemetry_client = value
diff --git a/samples/python-flask/21.corebot-app-insights/bots/resources/welcomeCard.json b/samples/python-flask/21.corebot-app-insights/bots/resources/welcomeCard.json
deleted file mode 100644
index 169f6328..00000000
--- a/samples/python-flask/21.corebot-app-insights/bots/resources/welcomeCard.json
+++ /dev/null
@@ -1,46 +0,0 @@
-{
-  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
-  "type": "AdaptiveCard",
-  "version": "1.0",
-  "body": [
-    {
-      "type": "Image",
-      "url": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQtB3AwMUeNoq4gUBGe6Ocj8kyh3bXa9ZbV7u1fVKQoyKFHdkqU",
-      "size": "stretch"
-    },
-    {
-      "type": "TextBlock",
-      "spacing": "medium",
-      "size": "default",
-      "weight": "bolder",
-      "text": "Welcome to Bot Framework!",
-      "wrap": true,
-      "maxLines": 0
-    },
-    {
-      "type": "TextBlock",
-      "size": "default",
-      "isSubtle": "yes",
-      "text": "Now that you have successfully run your bot, follow the links in this Adaptive Card to expand your knowledge of Bot Framework.",
-      "wrap": true,
-      "maxLines": 0
-    }
-  ],
-  "actions": [
-    {
-      "type": "Action.OpenUrl",
-      "title": "Get an overview",
-      "url": "https://docs.microsoft.com/en-us/azure/bot-service/?view=azure-bot-service-4.0"
-    },
-    {
-      "type": "Action.OpenUrl",
-      "title": "Ask a question",
-      "url": "https://stackoverflow.com/questions/tagged/botframework"
-    },
-    {
-      "type": "Action.OpenUrl",
-      "title": "Learn how to deploy",
-      "url": "https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-deploy-azure?view=azure-bot-service-4.0"
-    }
-  ]
-}
\ No newline at end of file
diff --git a/samples/python-flask/21.corebot-app-insights/cognitiveModels/FlightBooking.json b/samples/python-flask/21.corebot-app-insights/cognitiveModels/FlightBooking.json
deleted file mode 100644
index 5d1c9ec3..00000000
--- a/samples/python-flask/21.corebot-app-insights/cognitiveModels/FlightBooking.json
+++ /dev/null
@@ -1,226 +0,0 @@
-{
-  "luis_schema_version": "3.2.0",
-  "versionId": "0.1",
-  "name": "Airline Reservation",
-  "desc": "A LUIS model that uses intent and entities.",
-  "culture": "en-us",
-  "tokenizerVersion": "1.0.0",
-  "intents": [
-    {
-      "name": "Book flight"
-    },
-    {
-      "name": "Cancel"
-    },
-    {
-      "name": "None"
-    }
-  ],
-  "entities": [],
-  "composites": [
-    {
-      "name": "From",
-      "children": [
-        "Airport"
-      ],
-      "roles": []
-    },
-    {
-      "name": "To",
-      "children": [
-        "Airport"
-      ],
-      "roles": []
-    }
-  ],
-  "closedLists": [
-    {
-      "name": "Airport",
-      "subLists": [
-        {
-          "canonicalForm": "Paris",
-          "list": [
-            "paris"
-          ]
-        },
-        {
-          "canonicalForm": "London",
-          "list": [
-            "london"
-          ]
-        },
-        {
-          "canonicalForm": "Berlin",
-          "list": [
-            "berlin"
-          ]
-        },
-        {
-          "canonicalForm": "New York",
-          "list": [
-            "new york"
-          ]
-        }
-      ],
-      "roles": []
-    }
-  ],
-  "patternAnyEntities": [],
-  "regex_entities": [],
-  "prebuiltEntities": [
-    {
-      "name": "datetimeV2",
-      "roles": []
-    }
-  ],
-  "model_features": [],
-  "regex_features": [],
-  "patterns": [],
-  "utterances": [
-    {
-      "text": "book flight from london to paris on feb 14th",
-      "intent": "Book flight",
-      "entities": [
-        {
-          "entity": "To",
-          "startPos": 27,
-          "endPos": 31
-        },
-        {
-          "entity": "From",
-          "startPos": 17,
-          "endPos": 22
-        }
-      ]
-    },
-    {
-      "text": "book flight to berlin on feb 14th",
-      "intent": "Book flight",
-      "entities": [
-        {
-          "entity": "To",
-          "startPos": 15,
-          "endPos": 20
-        }
-      ]
-    },
-    {
-      "text": "book me a flight from london to paris",
-      "intent": "Book flight",
-      "entities": [
-        {
-          "entity": "From",
-          "startPos": 22,
-          "endPos": 27
-        },
-        {
-          "entity": "To",
-          "startPos": 32,
-          "endPos": 36
-        }
-      ]
-    },
-    {
-      "text": "bye",
-      "intent": "Cancel",
-      "entities": []
-    },
-    {
-      "text": "cancel booking",
-      "intent": "Cancel",
-      "entities": []
-    },
-    {
-      "text": "exit",
-      "intent": "Cancel",
-      "entities": []
-    },
-    {
-      "text": "flight to paris",
-      "intent": "Book flight",
-      "entities": [
-        {
-          "entity": "To",
-          "startPos": 10,
-          "endPos": 14
-        }
-      ]
-    },
-    {
-      "text": "flight to paris from london on feb 14th",
-      "intent": "Book flight",
-      "entities": [
-        {
-          "entity": "To",
-          "startPos": 10,
-          "endPos": 14
-        },
-        {
-          "entity": "From",
-          "startPos": 21,
-          "endPos": 26
-        }
-      ]
-    },
-    {
-      "text": "fly from berlin to paris on may 5th",
-      "intent": "Book flight",
-      "entities": [
-        {
-          "entity": "To",
-          "startPos": 19,
-          "endPos": 23
-        },
-        {
-          "entity": "From",
-          "startPos": 9,
-          "endPos": 14
-        }
-      ]
-    },
-    {
-      "text": "go to paris",
-      "intent": "Book flight",
-      "entities": [
-        {
-          "entity": "To",
-          "startPos": 6,
-          "endPos": 10
-        }
-      ]
-    },
-    {
-      "text": "going from paris to berlin",
-      "intent": "Book flight",
-      "entities": [
-        {
-          "entity": "To",
-          "startPos": 20,
-          "endPos": 25
-        },
-        {
-          "entity": "From",
-          "startPos": 11,
-          "endPos": 15
-        }
-      ]
-    },
-    {
-      "text": "ignore",
-      "intent": "Cancel",
-      "entities": []
-    },
-    {
-      "text": "travel to paris",
-      "intent": "Book flight",
-      "entities": [
-        {
-          "entity": "To",
-          "startPos": 10,
-          "endPos": 14
-        }
-      ]
-    }
-  ],
-  "settings": []
-}
\ No newline at end of file
diff --git a/samples/python-flask/21.corebot-app-insights/config.py b/samples/python-flask/21.corebot-app-insights/config.py
deleted file mode 100644
index d1a62370..00000000
--- a/samples/python-flask/21.corebot-app-insights/config.py
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Configuration for the bot."""
-class DefaultConfig(object):
-    """Configuration for the bot."""
-    PORT = 3978
-    APP_ID = ""
-    APP_PASSWORD = ""
-    LUIS_APP_ID = ""
-    # LUIS authoring key from LUIS portal or LUIS Cognitive Service subscription key
-    LUIS_API_KEY = ""
-    # LUIS endpoint host name, ie "https://westus.api.cognitive.microsoft.com"
-    LUIS_API_HOST_NAME = ""
-    APPINSIGHTS_INSTRUMENTATION_KEY = ""
diff --git a/samples/python-flask/21.corebot-app-insights/dialogs/__init__.py b/samples/python-flask/21.corebot-app-insights/dialogs/__init__.py
deleted file mode 100644
index e8c9730b..00000000
--- a/samples/python-flask/21.corebot-app-insights/dialogs/__init__.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Dialogs module"""
-from .booking_dialog import BookingDialog
-from .cancel_and_help_dialog import CancelAndHelpDialog
-from .date_resolver_dialog import DateResolverDialog
-from .main_dialog import MainDialog
-
-__all__ = [
-    'BookingDialog',
-    'CancelAndHelpDialog',
-    'DateResolverDialog',
-    'MainDialog']
diff --git a/samples/python-flask/21.corebot-app-insights/dialogs/booking_dialog.py b/samples/python-flask/21.corebot-app-insights/dialogs/booking_dialog.py
deleted file mode 100644
index f8a5a6ed..00000000
--- a/samples/python-flask/21.corebot-app-insights/dialogs/booking_dialog.py
+++ /dev/null
@@ -1,99 +0,0 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Flight booking dialog."""
-
-from botbuilder.dialogs import WaterfallDialog, WaterfallStepContext, DialogTurnResult
-from botbuilder.dialogs.prompts import ConfirmPrompt, TextPrompt, PromptOptions
-from botbuilder.core import MessageFactory, BotTelemetryClient, NullTelemetryClient
-from datatypes_date_time.timex import Timex
-from .cancel_and_help_dialog import CancelAndHelpDialog
-from .date_resolver_dialog import DateResolverDialog
-
-class BookingDialog(CancelAndHelpDialog):
-    """Flight booking implementation."""
-    def __init__(self, dialog_id: str = None,
-                 telemetry_client: BotTelemetryClient = NullTelemetryClient()):
-        super(BookingDialog, self).__init__(dialog_id or BookingDialog.__name__, telemetry_client)
-        self.telemetry_client = telemetry_client
-        text_prompt = TextPrompt(TextPrompt.__name__)
-        text_prompt.telemetry_client = telemetry_client
-
-        waterfall_dialog = WaterfallDialog(WaterfallDialog.__name__, [
-            self.destination_step,
-            self.origin_step,
-            self.travel_date_step,
-            #self.confirm_step,
-            self.final_step
-        ])
-        waterfall_dialog.telemetry_client = telemetry_client
-
-        self.add_dialog(text_prompt)
-        #self.add_dialog(ConfirmPrompt(ConfirmPrompt.__name__))
-        self.add_dialog(DateResolverDialog(DateResolverDialog.__name__, self.telemetry_client))
-        self.add_dialog(waterfall_dialog)
-
-        self.initial_dialog_id = WaterfallDialog.__name__
-
-    async def destination_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
-        """Prompt for destination."""
-        booking_details = step_context.options
-
-        if booking_details.destination is None:
-            return await step_context.prompt(TextPrompt.__name__,
-                                             PromptOptions(prompt=MessageFactory.text(
-                                                           'To what city would you like to travel?'))) # pylint: disable=line-too-long,bad-continuation
-        else:
-            return await step_context.next(booking_details.destination)
-
-    async def origin_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
-        """Prompt for origin city."""
-        booking_details = step_context.options
-
-        # Capture the response to the previous step's prompt
-        booking_details.destination = step_context.result
-        if booking_details.origin is None:
-            return await step_context.prompt(TextPrompt.__name__,
-                         PromptOptions(prompt=MessageFactory.text('From what city will you be travelling?'))) # pylint: disable=line-too-long,bad-continuation
-        else:
-            return await step_context.next(booking_details.origin)
-
-    async def travel_date_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
-        """Prompt for travel date.
-        This will use the DATE_RESOLVER_DIALOG."""
-
-        booking_details = step_context.options
-
-        # Capture the results of the previous step
-        booking_details.origin = step_context.result
-        if (not booking_details.travel_date or self.is_ambiguous(booking_details.travel_date)):
-            return await step_context.begin_dialog(DateResolverDialog.__name__, booking_details.travel_date) # pylint: disable=line-too-long
-        else:
-            return await step_context.next(booking_details.travel_date)
-
-    async def confirm_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
-        """Confirm the information the user has provided."""
-        booking_details = step_context.options
-
-        # Capture the results of the previous step
-        booking_details.travel_date = step_context.result
-        msg = f'Please confirm, I have you traveling to: { booking_details.destination }'\
-            f' from: { booking_details.origin } on: { booking_details.travel_date}.'
-
-        # Offer a YES/NO prompt.
-        return await step_context.prompt(ConfirmPrompt.__name__,
-                                         PromptOptions(prompt=MessageFactory.text(msg)))
-
-    async def final_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
-        """Complete the interaction and end the dialog."""
-        if step_context.result:
-            booking_details = step_context.options
-            booking_details.travel_date = step_context.result
-
-            return await step_context.end_dialog(booking_details)
-        else:
-            return await step_context.end_dialog()
-
-    def is_ambiguous(self, timex: str) -> bool:
-        """Ensure time is correct."""
-        timex_property = Timex(timex)
-        return 'definite' not in timex_property.types
diff --git a/samples/python-flask/21.corebot-app-insights/dialogs/cancel_and_help_dialog.py b/samples/python-flask/21.corebot-app-insights/dialogs/cancel_and_help_dialog.py
deleted file mode 100644
index 7596a8ca..00000000
--- a/samples/python-flask/21.corebot-app-insights/dialogs/cancel_and_help_dialog.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Handle cancel and help intents."""
-
-from botbuilder.core import BotTelemetryClient, NullTelemetryClient
-from botbuilder.dialogs import ComponentDialog, DialogContext, DialogTurnResult, DialogTurnStatus
-from botbuilder.schema import ActivityTypes
-
-
-class CancelAndHelpDialog(ComponentDialog):
-    """Implementation of handling cancel and help."""
-    def __init__(self, dialog_id: str,
-                 telemetry_client: BotTelemetryClient = NullTelemetryClient()):
-        super(CancelAndHelpDialog, self).__init__(dialog_id)
-        self.telemetry_client = telemetry_client
-
-    async def on_begin_dialog(self, inner_dc: DialogContext, options: object) -> DialogTurnResult:
-        result = await self.interrupt(inner_dc)
-        if result is not None:
-            return result
-
-        return await super(CancelAndHelpDialog, self).on_begin_dialog(inner_dc, options)
-
-    async def on_continue_dialog(self, inner_dc: DialogContext) -> DialogTurnResult:
-        result = await self.interrupt(inner_dc)
-        if result is not None:
-            return result
-
-        return await super(CancelAndHelpDialog, self).on_continue_dialog(inner_dc)
-
-    async def interrupt(self, inner_dc: DialogContext) -> DialogTurnResult:
-        """Detect interruptions."""
-        if inner_dc.context.activity.type == ActivityTypes.message:
-            text = inner_dc.context.activity.text.lower()
-
-            if text == 'help' or text == '?':
-                await inner_dc.context.send_activity("Show Help...")
-                return DialogTurnResult(DialogTurnStatus.Waiting)
-
-            if text == 'cancel' or text == 'quit':
-                await inner_dc.context.send_activity("Cancelling")
-                return await inner_dc.cancel_all_dialogs()
-
-        return None
diff --git a/samples/python-flask/21.corebot-app-insights/dialogs/date_resolver_dialog.py b/samples/python-flask/21.corebot-app-insights/dialogs/date_resolver_dialog.py
deleted file mode 100644
index 601657c6..00000000
--- a/samples/python-flask/21.corebot-app-insights/dialogs/date_resolver_dialog.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Handle date/time resolution for booking dialog."""
-from botbuilder.core import MessageFactory, BotTelemetryClient, NullTelemetryClient
-from botbuilder.dialogs import WaterfallDialog, DialogTurnResult, WaterfallStepContext
-from botbuilder.dialogs.prompts import DateTimePrompt, PromptValidatorContext, \
-    PromptOptions, DateTimeResolution
-from datatypes_date_time.timex import Timex
-from .cancel_and_help_dialog import CancelAndHelpDialog
-
-class DateResolverDialog(CancelAndHelpDialog):
-    """Resolve the date"""
-    def __init__(self, dialog_id: str = None,
-                 telemetry_client: BotTelemetryClient = NullTelemetryClient()):
-        super(DateResolverDialog, self).__init__(dialog_id or DateResolverDialog.__name__,
-                                                 telemetry_client)
-        self.telemetry_client = telemetry_client
-
-        date_time_prompt = DateTimePrompt(DateTimePrompt.__name__,
-                                          DateResolverDialog.datetime_prompt_validator)
-        date_time_prompt.telemetry_client = telemetry_client
-
-        waterfall_dialog = WaterfallDialog(WaterfallDialog.__name__ + '2', [
-            self.initial_step,
-            self.final_step
-        ])
-        waterfall_dialog.telemetry_client = telemetry_client
-
-        self.add_dialog(date_time_prompt)
-        self.add_dialog(waterfall_dialog)
-
-        self.initial_dialog_id = WaterfallDialog.__name__ + '2'
-
-    async def initial_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
-        """Prompt for the date."""
-        timex = step_context.options
-
-        prompt_msg = 'On what date would you like to travel?'
-        reprompt_msg = "I'm sorry, for best results, please enter your travel "\
-                       "date including the month, day and year."
-
-        if timex is None:
-            # We were not given any date at all so prompt the user.
-            return await step_context.prompt(DateTimePrompt.__name__,
-                PromptOptions(  # pylint: disable=bad-continuation
-                    prompt=MessageFactory.text(prompt_msg),
-                    retry_prompt=MessageFactory.text(reprompt_msg)
-                ))
-        else:
-            # We have a Date we just need to check it is unambiguous.
-            if 'definite' in Timex(timex).types:
-                # This is essentially a "reprompt" of the data we were given up front.
-                return await step_context.prompt(DateTimePrompt.__name__,
-                                                 PromptOptions(prompt=reprompt_msg))
-            else:
-                return await step_context.next(DateTimeResolution(timex=timex))
-
-    async def final_step(self, step_context: WaterfallStepContext):
-        """Cleanup - set final return value and end dialog."""
-        timex = step_context.result[0].timex
-        return await step_context.end_dialog(timex)
-
-    @staticmethod
-    async def datetime_prompt_validator(prompt_context: PromptValidatorContext) -> bool:
-        """ Validate the date provided is in proper form. """
-        if prompt_context.recognized.succeeded:
-            timex = prompt_context.recognized.value[0].timex.split('T')[0]
-
-            # TODO: Needs TimexProperty
-            return 'definite' in Timex(timex).types
-
-        return False
diff --git a/samples/python-flask/21.corebot-app-insights/dialogs/main_dialog.py b/samples/python-flask/21.corebot-app-insights/dialogs/main_dialog.py
deleted file mode 100644
index 617fb8cb..00000000
--- a/samples/python-flask/21.corebot-app-insights/dialogs/main_dialog.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Main dialog. """
-
-from botbuilder.core import BotTelemetryClient, NullTelemetryClient
-from botbuilder.dialogs import ComponentDialog, WaterfallDialog, WaterfallStepContext,\
-    DialogTurnResult
-from botbuilder.dialogs.prompts import TextPrompt, PromptOptions
-from botbuilder.core import MessageFactory
-from booking_details import BookingDetails
-from helpers.luis_helper import LuisHelper
-from .booking_dialog import BookingDialog
-
-
-class MainDialog(ComponentDialog):
-    """Main dialog. """
-    def __init__(self, configuration: dict, dialog_id: str = None,
-                 telemetry_client: BotTelemetryClient = NullTelemetryClient()):
-        super(MainDialog, self).__init__(dialog_id or MainDialog.__name__)
-
-        self._configuration = configuration
-        self.telemetry_client = telemetry_client
-
-        text_prompt = TextPrompt(TextPrompt.__name__)
-        text_prompt.telemetry_client = self.telemetry_client
-
-        booking_dialog = BookingDialog(telemetry_client=self._telemetry_client)
-        booking_dialog.telemetry_client = self.telemetry_client
-
-        wf_dialog = WaterfallDialog('WFDialog', [
-            self.intro_step,
-            self.act_step,
-            self.final_step
-        ])
-        wf_dialog.telemetry_client = self.telemetry_client
-
-        self.add_dialog(text_prompt)
-        self.add_dialog(booking_dialog)
-        self.add_dialog(wf_dialog)
-
-        self.initial_dialog_id = 'WFDialog'
-
-    async def intro_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
-        """Initial prompt."""
-        if (not self._configuration.get("LUIS_APP_ID", "") or
-                not self._configuration.get("LUIS_API_KEY", "") or
-                not self._configuration.get("LUIS_API_HOST_NAME", "")):
-            await step_context.context.send_activity(
-                MessageFactory.text("NOTE: LUIS is not configured. To enable all"\
-                    " capabilities, add 'LUIS_APP_ID', 'LUIS_API_KEY' and 'LUIS_API_HOST_NAME'"\
-                    " to the config.py file."))
-
-            return await step_context.next(None)
-        else:
-            return await step_context.prompt(TextPrompt.__name__,
-                PromptOptions(prompt=MessageFactory.text("What can I help you with today?"))) # pylint: disable=bad-continuation
-
-
-    async def act_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
-        """Use language understanding to gather details about booking."""
-        # Call LUIS and gather any potential booking details. (Note the TurnContext
-        # has the response to the prompt.)
-        booking_details = await LuisHelper.execute_luis_query(self._configuration,\
-            step_context.context, self.telemetry_client) if step_context.result is not None\
-            else BookingDetails() # pylint: disable=bad-continuation
-
-        # In this sample we only have a single Intent we are concerned with. However,
-        # typically a scenario will have multiple different Intents each corresponding
-        # to starting a different child Dialog.
-
-
-        # Run the BookingDialog giving it whatever details we have from the
-        # model.  The dialog will prompt to find out the remaining details.
-        return await step_context.begin_dialog(BookingDialog.__name__, booking_details)
-
-    async def final_step(self, step_context: WaterfallStepContext) -> DialogTurnResult:
-        """Complete dialog.
-        At this step, with details from the user, display the completed
-        flight booking to the user.
-        """
-        # If the child dialog ("BookingDialog") was cancelled or the user failed
-        # to confirm, the Result here will be null.
-        if step_context.result is not None:
-            result = step_context.result
-
-            # Now we have all the booking details call the booking service.
-            # If the call to the booking service was successful tell the user.
-            #time_property = Timex(result.travel_date)
-            #travel_date_msg = time_property.to_natural_language(datetime.now())
-            msg = f'I have you booked to {result.destination} from'\
-                f' {result.origin} on {result.travel_date}.'
-            await step_context.context.send_activity(MessageFactory.text(msg))
-        else:
-            await step_context.context.send_activity(MessageFactory.text("Thank you."))
-        return await step_context.end_dialog()
diff --git a/samples/python-flask/21.corebot-app-insights/helpers/__init__.py b/samples/python-flask/21.corebot-app-insights/helpers/__init__.py
deleted file mode 100644
index 6abcfc9c..00000000
--- a/samples/python-flask/21.corebot-app-insights/helpers/__init__.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Helpers module."""
-
-from . import activity_helper, luis_helper, dialog_helper
-
-__all__ = [
-    'activity_helper',
-    'dialog_helper',
-    'luis_helper']
\ No newline at end of file
diff --git a/samples/python-flask/21.corebot-app-insights/helpers/activity_helper.py b/samples/python-flask/21.corebot-app-insights/helpers/activity_helper.py
deleted file mode 100644
index 144f6365..00000000
--- a/samples/python-flask/21.corebot-app-insights/helpers/activity_helper.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Helper to create reply object."""
-
-from datetime import datetime
-from botbuilder.schema import Activity, ActivityTypes, ChannelAccount, ConversationAccount
-
-def create_activity_reply(activity: Activity, text: str = None, locale: str = None):
-    """Helper to create reply object."""
-    return Activity(
-        type=ActivityTypes.message,
-        timestamp=datetime.utcnow(),
-        from_property=ChannelAccount(id=getattr(activity.recipient, 'id', None),
-                                     name=getattr(activity.recipient, 'name', None)),
-        recipient=ChannelAccount(id=activity.from_property.id, name=activity.from_property.name),
-        reply_to_id=activity.id,
-        service_url=activity.service_url,
-        channel_id=activity.channel_id,
-        conversation=ConversationAccount(is_group=activity.conversation.is_group,
-                                         id=activity.conversation.id,
-                                         name=activity.conversation.name),
-        text=text or '',
-        locale=locale or '',
-        attachments=[],
-        entities=[]
-    )
diff --git a/samples/python-flask/21.corebot-app-insights/helpers/dialog_helper.py b/samples/python-flask/21.corebot-app-insights/helpers/dialog_helper.py
deleted file mode 100644
index 71ae0690..00000000
--- a/samples/python-flask/21.corebot-app-insights/helpers/dialog_helper.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-"""Utility to run dialogs."""
-from botbuilder.core import StatePropertyAccessor, TurnContext
-from botbuilder.dialogs import Dialog, DialogSet, DialogTurnStatus
-
-class DialogHelper:
-    """Dialog Helper implementation."""
-
-    @staticmethod
-    async def run_dialog(dialog: Dialog, turn_context: TurnContext, accessor: StatePropertyAccessor): # pylint: disable=line-too-long
-        """Run dialog."""
-        dialog_set = DialogSet(accessor)
-        dialog_set.add(dialog)
-
-        dialog_context = await dialog_set.create_context(turn_context)
-        results = await dialog_context.continue_dialog()
-        if results.status == DialogTurnStatus.Empty:
-            await dialog_context.begin_dialog(dialog.id)
diff --git a/samples/python-flask/21.corebot-app-insights/helpers/luis_helper.py b/samples/python-flask/21.corebot-app-insights/helpers/luis_helper.py
deleted file mode 100644
index 20da0579..00000000
--- a/samples/python-flask/21.corebot-app-insights/helpers/luis_helper.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-
-"""Helper to call LUIS service."""
-from botbuilder.ai.luis import LuisRecognizer, LuisApplication, LuisPredictionOptions
-from botbuilder.core import TurnContext, BotTelemetryClient, NullTelemetryClient
-
-from booking_details import BookingDetails
-
-# pylint: disable=line-too-long
-class LuisHelper:
-    """LUIS helper implementation."""
-    @staticmethod
-    async def execute_luis_query(configuration, turn_context: TurnContext,
-                                 telemetry_client: BotTelemetryClient = None) -> BookingDetails:
-        """Invoke LUIS service to perform prediction/evaluation of utterance."""
-        booking_details = BookingDetails()
-
-        # pylint:disable=broad-except
-        try:
-            luis_application = LuisApplication(
-                configuration.get('LUIS_APP_ID'),
-                configuration.get('LUIS_API_KEY'),
-                configuration.get('LUIS_API_HOST_NAME')
-            )
-            options = LuisPredictionOptions()
-            options.telemetry_client = telemetry_client if telemetry_client is not None else NullTelemetryClient()
-            recognizer = LuisRecognizer(luis_application, prediction_options=options)
-            recognizer_result = await recognizer.recognize(turn_context)
-            print(f'Recognize Result: {recognizer_result}')
-
-            if recognizer_result.intents:
-                intent = sorted(recognizer_result.intents, key=recognizer_result.intents.get, reverse=True)[:1][0]
-                if intent == 'Book_flight':
-                    # We need to get the result from the LUIS JSON which at every level returns an array.
-                    to_entities = recognizer_result.entities.get("$instance", {}).get("To", [])
-                    if to_entities:
-                        booking_details.destination = to_entities[0]['text']
-                    from_entities = recognizer_result.entities.get("$instance", {}).get("From", [])
-                    if from_entities:
-                        booking_details.origin = from_entities[0]['text']
-
-                    # This value will be a TIMEX. And we are only interested in a Date so grab the first result and drop the Time part.
-                    # TIMEX is a format that represents DateTime expressions that include some ambiguity. e.g. missing a Year.
-                    date_entities = recognizer_result.entities.get("$instance", {}).get("datetime", [])
-                    if date_entities:
-                        booking_details.travel_date = None  # Set when we get a timex format
-        except Exception as exception:
-            print(exception)
-
-        return booking_details
diff --git a/samples/python-flask/21.corebot-app-insights/main.py b/samples/python-flask/21.corebot-app-insights/main.py
deleted file mode 100644
index 9c27431c..00000000
--- a/samples/python-flask/21.corebot-app-insights/main.py
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-
-"""
-This sample shows how to create a bot that demonstrates the following:
-- Use [LUIS](https://www.luis.ai) to implement core AI capabilities.
-- Implement a multi-turn conversation using Dialogs.
-- Handle user interruptions for such things as `Help` or `Cancel`.
-- Prompt for and validate requests for information from the user.
-
-"""
-
-import asyncio
-from flask import Flask, request, Response
-from botbuilder.core import (BotFrameworkAdapter, BotFrameworkAdapterSettings,
-                             ConversationState, MemoryStorage, UserState, TurnContext)
-from botbuilder.schema import (Activity)
-from botbuilder.applicationinsights import ApplicationInsightsTelemetryClient
-from botbuilder.applicationinsights.flask import BotTelemetryMiddleware
-
-from dialogs import MainDialog
-from bots import DialogAndWelcomeBot
-
-
-LOOP = asyncio.get_event_loop()
-APP = Flask(__name__, instance_relative_config=True)
-APP.config.from_object('config.DefaultConfig')
-APP.wsgi_app = BotTelemetryMiddleware(APP.wsgi_app)
-
-SETTINGS = BotFrameworkAdapterSettings(APP.config['APP_ID'], APP.config['APP_PASSWORD'])
-ADAPTER = BotFrameworkAdapter(SETTINGS)
-
-# pylint:disable=unused-argument
-async def on_error(context: TurnContext, error: Exception):
-    """ Catch-all for errors."""
-    # Send a message to the user
-    await context.send_activity('Oops. Something went wrong!')
-    # Clear out state
-    await CONVERSATION_STATE.delete(context)
-
-ADAPTER.on_turn_error = on_error
-
-# Create MemoryStorage, UserState and ConversationState
-MEMORY = MemoryStorage()
-
-USER_STATE = UserState(MEMORY)
-CONVERSATION_STATE = ConversationState(MEMORY)
-INSTRUMENTATION_KEY = APP.config['APPINSIGHTS_INSTRUMENTATION_KEY']
-TELEMETRY_CLIENT = ApplicationInsightsTelemetryClient(INSTRUMENTATION_KEY)
-DIALOG = MainDialog(APP.config, telemetry_client=TELEMETRY_CLIENT)
-BOT = DialogAndWelcomeBot(CONVERSATION_STATE, USER_STATE, DIALOG, TELEMETRY_CLIENT)
-
-
-@APP.route('/api/messages', methods=['POST'])
-def messages():
-    """Main bot message handler."""
-    if request.headers['Content-Type'] == 'application/json':
-        body = request.json
-    else:
-        return Response(status=415)
-
-    activity = Activity().deserialize(body)
-    auth_header = request.headers['Authorization'] if 'Authorization' in request.headers else ''
-
-    async def aux_func(turn_context):
-        await BOT.on_turn(turn_context)
-
-    try:
-        future = asyncio.ensure_future(ADAPTER.process_activity(activity, auth_header, aux_func),
-                                       loop=LOOP)
-        LOOP.run_until_complete(future)
-        return Response(status=201)
-    except Exception as exception:
-        raise exception
-
-
-if __name__ == "__main__":
-    try:
-        APP.run(debug=True, port=APP.config["PORT"])
-
-    except Exception as exception:
-        raise exception
diff --git a/samples/python-flask/21.corebot-app-insights/requirements.txt b/samples/python-flask/21.corebot-app-insights/requirements.txt
deleted file mode 100644
index ffcf72c6..00000000
--- a/samples/python-flask/21.corebot-app-insights/requirements.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-Flask>=1.0.2
-asyncio>=3.4.3
-requests>=2.18.1
-botframework-connector>=4.4.0.b1
-botbuilder-schema>=4.4.0.b1
-botbuilder-core>=4.4.0.b1
-botbuilder-dialogs>=4.4.0.b1
-botbuilder-ai>=4.4.0.b1
-botbuilder-applicationinsights>=4.4.0.b1
-datatypes-date-time>=1.0.0.a1
-azure-cognitiveservices-language-luis>=0.2.0
-msrest>=0.6.6
-


Test patch:
diff --git a/libraries/botbuilder-core/botbuilder/core/adapters/test_adapter.py b/libraries/botbuilder-core/botbuilder/core/adapters/test_adapter.py
index cf4896fa..786c8f17 100644
--- a/libraries/botbuilder-core/botbuilder/core/adapters/test_adapter.py
+++ b/libraries/botbuilder-core/botbuilder/core/adapters/test_adapter.py
@@ -103,7 +103,7 @@ class TestAdapter(BotAdapter):
             if value is not None and key != 'additional_properties':
                 setattr(request, key, value)
 
-            request.type = ActivityTypes.message
+        request.type = request.type or ActivityTypes.message
         if not request.id:
             self._next_id += 1
             request.id = str(self._next_id)
diff --git a/libraries/botbuilder-dialogs/tests/test_activity_prompt.py b/libraries/botbuilder-dialogs/tests/test_activity_prompt.py
index deeb02c0..8ff401dd 100644
--- a/libraries/botbuilder-dialogs/tests/test_activity_prompt.py
+++ b/libraries/botbuilder-dialogs/tests/test_activity_prompt.py
@@ -2,35 +2,189 @@
 # Licensed under the MIT License.
 
 import aiounittest
-from botbuilder.dialogs.prompts import ActivityPrompt, NumberPrompt, PromptOptions, PromptRecognizerResult
-from botbuilder.schema import Activity, InputHints
+import unittest
 
-from botbuilder.core.turn_context import TurnContext
+from typing import Callable
+from botbuilder.dialogs.prompts import (ActivityPrompt, NumberPrompt, PromptOptions, PromptRecognizerResult,
+                                        PromptValidatorContext)
+from botbuilder.schema import Activity, InputHints, ActivityTypes
+
+from botbuilder.core import ConversationState, MemoryStorage, TurnContext, MessageFactory
 from botbuilder.core.adapters import TestAdapter
+from botbuilder.dialogs import DialogSet, DialogTurnStatus, DialogReason
+
+
+async def validator(prompt_context: PromptValidatorContext):
+    tester = unittest.TestCase()
+    tester.assertTrue(prompt_context.attempt_count > 0)
+
+    activity = prompt_context.recognized.value
+
+    if activity.type == ActivityTypes.event:
+        if int(activity.value) == 2:
+            prompt_context.recognized.value = MessageFactory.text(str(activity.value))
+            return True
+    else:
+        await prompt_context.context.send_activity("Please send an 'event'-type Activity with a value of 2.")
+
+    return False
+
 
 class SimpleActivityPrompt(ActivityPrompt):
-    pass
+    def __init__(self, dialog_id: str, validator: Callable[[PromptValidatorContext], bool]):
+        super().__init__(dialog_id, validator)
+
 
 class ActivityPromptTests(aiounittest.AsyncTestCase):
-    async def test_does_the_things(self):
-        my_activity = Activity(type='message', text='I am activity message!')
-        my_retry_prompt = Activity(type='message', id='ididretry', text='retry text hurrr')
-        options = PromptOptions(prompt=my_activity, retry_prompt=my_retry_prompt)
-        activity_promptyy = ActivityPrompt('myId', 'validator thing')
-
-        my_context = TurnContext(TestAdapter(), my_activity)
-        my_state = {'stringy': {'nestedkey': 'nestedvalue'} }
-        
-        await activity_promptyy.on_prompt(my_context, state=my_state, options=options, isRetry=True)
-
-        print('placeholder print')
-
-        pass
-
-    # def test_activity_prompt_with_empty_id_should_fail(self):
-    #     empty_id = ''
-    #     text_prompt = SimpleActivityPrompt(empty_id, self.validator)
-    
-    # async def validator(self):
-    #     return True
-        
\ No newline at end of file
+
+    def test_activity_prompt_with_empty_id_should_fail(self):
+        empty_id = ''
+        with self.assertRaises(TypeError):
+            SimpleActivityPrompt(empty_id, validator)
+
+    def test_activity_prompt_with_none_id_should_fail(self):
+        none_id = None
+        with self.assertRaises(TypeError):
+            SimpleActivityPrompt(none_id, validator)
+
+    def test_activity_prompt_with_none_validator_should_fail(self):
+        none_validator = None
+        with self.assertRaises(TypeError):
+            SimpleActivityPrompt('EventActivityPrompt', none_validator)
+
+    async def test_basic_activity_prompt(self):
+        async def exec_test(turn_context: TurnContext):
+            dc = await dialogs.create_context(turn_context)
+
+            results = await dc.continue_dialog()
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(prompt=Activity(type=ActivityTypes.message, text='please send an event.'))
+                await dc.prompt('EventActivityPrompt', options)
+            elif results.status == DialogTurnStatus.Complete:
+                await turn_context.send_activity(results.result)
+
+            await convo_state.save_changes(turn_context)
+
+        # Initialize TestAdapter.
+        adapter = TestAdapter(exec_test)
+
+        # Create ConversationState with MemoryStorage and register the state as middleware.
+        convo_state = ConversationState(MemoryStorage())
+
+        # Create a DialogState property, DialogSet and AttachmentPrompt.
+        dialog_state = convo_state.create_property('dialog_state')
+        dialogs = DialogSet(dialog_state)
+        dialogs.add(SimpleActivityPrompt('EventActivityPrompt', validator))
+
+        event_activity = Activity(type=ActivityTypes.event, value=2)
+
+        step1 = await adapter.send('hello')
+        step2 = await step1.assert_reply('please send an event.')
+        step3 = await step2.send(event_activity)
+        await step3.assert_reply('2')
+
+    async def test_retry_activity_prompt(self):
+        async def exec_test(turn_context: TurnContext):
+            dc = await dialogs.create_context(turn_context)
+
+            results = await dc.continue_dialog()
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(prompt=Activity(type=ActivityTypes.message, text='please send an event.'))
+                await dc.prompt('EventActivityPrompt', options)
+            elif results.status == DialogTurnStatus.Complete:
+                await turn_context.send_activity(results.result)
+
+            await convo_state.save_changes(turn_context)
+
+        # Initialize TestAdapter.
+        adapter = TestAdapter(exec_test)
+
+        # Create ConversationState with MemoryStorage and register the state as middleware.
+        convo_state = ConversationState(MemoryStorage())
+
+        # Create a DialogState property, DialogSet and AttachmentPrompt.
+        dialog_state = convo_state.create_property('dialog_state')
+        dialogs = DialogSet(dialog_state)
+        dialogs.add(SimpleActivityPrompt('EventActivityPrompt', validator))
+
+        event_activity = Activity(type=ActivityTypes.event, value=2)
+
+        step1 = await adapter.send('hello')
+        step2 = await step1.assert_reply('please send an event.')
+        step3 = await step2.send('hello again')
+        step4 = await step3.assert_reply("Please send an 'event'-type Activity with a value of 2.")
+        step5 = await step4.send(event_activity)
+        await step5.assert_reply('2')
+
+    async def test_activity_prompt_should_return_dialog_end_if_validation_failed(self):
+        async def exec_test(turn_context: TurnContext):
+            dc = await dialogs.create_context(turn_context)
+
+            results = await dc.continue_dialog()
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(
+                    prompt=Activity(type=ActivityTypes.message, text='please send an event.'),
+                    retry_prompt=Activity(type=ActivityTypes.message, text='event not received.')
+                )
+                await dc.prompt('EventActivityPrompt', options)
+            elif results.status == DialogTurnStatus.Complete:
+                await turn_context.send_activity(results.result)
+
+            await convo_state.save_changes(turn_context)
+
+        async def aux_validator(prompt_context: PromptValidatorContext):
+            assert prompt_context, 'Validator missing prompt_context'
+            return False
+
+        # Initialize TestAdapter.
+        adapter = TestAdapter(exec_test)
+
+
+
+        # Create ConversationState with MemoryStorage and register the state as middleware.
+        convo_state = ConversationState(MemoryStorage())
+
+        # Create a DialogState property, DialogSet and AttachmentPrompt.
+        dialog_state = convo_state.create_property('dialog_state')
+        dialogs = DialogSet(dialog_state)
+        dialogs.add(SimpleActivityPrompt('EventActivityPrompt', aux_validator))
+
+        step1 = await adapter.send('hello')
+        step2 = await step1.assert_reply('please send an event.')
+        step3 = await step2.send('test')
+        await step3.assert_reply('event not received.')
+
+    async def test_activity_prompt_resume_dialog_should_return_dialog_end(self):
+        async def exec_test(turn_context: TurnContext):
+            dc = await dialogs.create_context(turn_context)
+
+            results = await dc.continue_dialog()
+            if results.status == DialogTurnStatus.Empty:
+                options = PromptOptions(prompt=Activity(type=ActivityTypes.message, text='please send an event.'))
+                await dc.prompt('EventActivityPrompt', options)
+
+            second_results = await event_prompt.resume_dialog(dc, DialogReason.NextCalled)
+
+            assert second_results.status == DialogTurnStatus.Waiting, 'resume_dialog did not returned Dialog.EndOfTurn'
+
+            await convo_state.save_changes(turn_context)
+
+        async def aux_validator(prompt_context: PromptValidatorContext):
+            assert prompt_context, 'Validator missing prompt_context'
+            return False
+
+        # Initialize TestAdapter.
+        adapter = TestAdapter(exec_test)
+
+        # Create ConversationState with MemoryStorage and register the state as middleware.
+        convo_state = ConversationState(MemoryStorage())
+
+        # Create a DialogState property, DialogSet and AttachmentPrompt.
+        dialog_state = convo_state.create_property('dialog_state')
+        dialogs = DialogSet(dialog_state)
+        event_prompt = SimpleActivityPrompt('EventActivityPrompt', aux_validator)
+        dialogs.add(event_prompt)
+
+        step1 = await adapter.send('hello')
+        step2 = await step1.assert_reply('please send an event.')
+        await step2.assert_reply('please send an event.')