From cd1485f7e4acb0c5e720c37bc904256a19dce3ed Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 10 Jan 2023 14:22:24 +0530 Subject: [PATCH 01/94] modified logic in run, slot management in _query_objects and _query_attribute functions --- rasa_sdk/knowledge_base/actions.py | 48 +++++++++++++++++++----------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 59c90812d..a70d16e41 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -13,6 +13,7 @@ SLOT_LISTED_OBJECTS, get_object_name, get_attribute_slots, + get_object_type_dynamic, ) from rasa_sdk import utils from rasa_sdk.executor import CollectingDispatcher @@ -106,9 +107,8 @@ async def run( tracker: Tracker, domain: "DomainDict", ) -> List[Dict[Text, Any]]: - """Executes this action. - - If the user ask a question about an attribute, + """ + Executes this action. If the user ask a question about an attribute, the knowledge base is queried for that attribute. Otherwise, if no attribute was detected in the request or the user is talking about a new object type, multiple objects of the requested type are returned from the @@ -125,20 +125,35 @@ async def run( object_type = tracker.get_slot(SLOT_OBJECT_TYPE) last_object_type = tracker.get_slot(SLOT_LAST_OBJECT_TYPE) attribute = tracker.get_slot(SLOT_ATTRIBUTE) + has_mention = tracker.get_slot(SLOT_MENTION) is not None new_request = object_type != last_object_type if not object_type: - # object type always needs to be set as this is needed to query the - # knowledge base - dispatcher.utter_message(response="utter_ask_rephrase") - return [] + # sets the object type dynamically from entities if object_type is not found + # in user query + object_types = await utils.call_potential_coroutine( + self.knowledge_base.get_object_types() + ) + object_type_dynamic = await get_object_type_dynamic(tracker, object_types) - if not attribute or new_request: - return await self._query_objects(dispatcher, object_type, tracker) - elif attribute: + if object_type: + if not attribute or new_request: + return await self._query_objects(dispatcher, object_type, tracker) + + elif attribute: + return await self._query_attribute( + dispatcher, object_type, attribute, tracker + ) + + if object_type_dynamic and attribute: return await self._query_attribute( - dispatcher, object_type, attribute, tracker + dispatcher, object_type_dynamic, attribute, tracker + ) + + if last_object_type and has_mention and attribute: + return await self._query_attribute( + dispatcher, last_object_type, attribute, tracker ) dispatcher.utter_message(response="utter_ask_rephrase") @@ -161,7 +176,6 @@ async def _query_objects( object_attributes = await utils.call_potential_coroutine( self.knowledge_base.get_attributes_of_object(object_type) ) - # get all set attribute slots of the object type to be able to filter the # list of objects attributes = get_attribute_slots(tracker, object_attributes) @@ -169,7 +183,6 @@ async def _query_objects( objects = await utils.call_potential_coroutine( self.knowledge_base.get_objects(object_type, attributes) ) - await utils.call_potential_coroutine( self.utter_objects(dispatcher, object_type, objects) ) @@ -182,9 +195,8 @@ async def _query_objects( ) last_object = None if len(objects) > 1 else objects[0][key_attribute] - slots = [ - SlotSet(SLOT_OBJECT_TYPE, object_type), + SlotSet(SLOT_OBJECT_TYPE, None), SlotSet(SLOT_MENTION, None), SlotSet(SLOT_ATTRIBUTE, None), SlotSet(SLOT_LAST_OBJECT, last_object), @@ -213,14 +225,14 @@ async def _query_attribute( Returns: list of slots """ - object_name = get_object_name( tracker, + object_type, self.knowledge_base.ordinal_mention_mapping, self.use_last_object_mention, ) - if object_name is None or not attribute: + if not object_name or not attribute: dispatcher.utter_message(response="utter_ask_rephrase") return [SlotSet(SLOT_MENTION, None)] @@ -253,7 +265,7 @@ async def _query_attribute( ) slots = [ - SlotSet(SLOT_OBJECT_TYPE, object_type), + SlotSet(SLOT_OBJECT_TYPE, None), SlotSet(SLOT_ATTRIBUTE, None), SlotSet(SLOT_MENTION, None), SlotSet(SLOT_LAST_OBJECT, object_identifier), From 751bc1f00f0ad46de942416b4ad8769da7806027 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 10 Jan 2023 14:22:58 +0530 Subject: [PATCH 02/94] added get_object_types function --- rasa_sdk/knowledge_base/storage.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/rasa_sdk/knowledge_base/storage.py b/rasa_sdk/knowledge_base/storage.py index d49318ee2..eb2558ee8 100644 --- a/rasa_sdk/knowledge_base/storage.py +++ b/rasa_sdk/knowledge_base/storage.py @@ -110,6 +110,14 @@ async def get_object( """ raise NotImplementedError("Method is not implemented.") + async def get_object_types(self) -> List[Text]: + """ + Returns a list of all object types that belong to the knowledge base. + + Returns: list of object types + """ + raise NotImplementedError("Method is not implemented.") + class InMemoryKnowledgeBase(KnowledgeBase): def __init__(self, data_file: Text) -> None: @@ -202,10 +210,10 @@ async def get_object( self, object_type: Text, object_identifier: Text ) -> Optional[Dict[Text, Any]]: if object_type not in self.data: + return None objects = self.data[object_type] - key_attribute = await utils.call_potential_coroutine( self.get_key_attribute_of_object(object_type) ) @@ -222,6 +230,7 @@ async def get_object( # if the object was referred to directly, we need to compare the representation # of each object with the given object identifier if not objects_of_interest: + repr_function = await utils.call_potential_coroutine( self.get_representation_function_of_object(object_type) ) @@ -234,10 +243,13 @@ async def get_object( ) ) - if not objects_of_interest or len(objects_of_interest) > 1: - # TODO: - # if multiple objects are found, the objects could be shown - # to the user. the user then needs to clarify what object he meant. - return None + # if not objects_of_interest or len(objects_of_interest) > 1: + # # TODO: + # # if multiple objects are found, the objects could be shown + # # to the user. the user then needs to clarify what object he meant. + # return None return objects_of_interest[0] + + async def get_object_types(self) -> List[Text]: + return list(self.data.keys()) From c611f0e26b37a226710ec04c831b7c0ac2458b37 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 10 Jan 2023 14:23:44 +0530 Subject: [PATCH 03/94] added get_object_type_dynamic function --- rasa_sdk/knowledge_base/utils.py | 35 ++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/rasa_sdk/knowledge_base/utils.py b/rasa_sdk/knowledge_base/utils.py index 93bda123f..32c99de1c 100644 --- a/rasa_sdk/knowledge_base/utils.py +++ b/rasa_sdk/knowledge_base/utils.py @@ -15,6 +15,7 @@ def get_object_name( tracker: "Tracker", + object_type: Text, ordinal_mention_mapping: Dict[Text, Callable], use_last_object_mention: bool = True, ) -> Optional[Text]: @@ -34,11 +35,10 @@ def get_object_name( knowledge base) """ mention = tracker.get_slot(SLOT_MENTION) - object_type = tracker.get_slot(SLOT_OBJECT_TYPE) # the user referred to the object by a mention, such as "first one" if mention: - return resolve_mention(tracker, ordinal_mention_mapping) + return resolve_mention(tracker, ordinal_mention_mapping, object_type) # check whether the user referred to the objet by its name object_name = tracker.get_slot(object_type) @@ -54,7 +54,7 @@ def get_object_name( def resolve_mention( - tracker: "Tracker", ordinal_mention_mapping: Dict[Text, Callable] + tracker: "Tracker", ordinal_mention_mapping: Dict[Text, Callable], object_type: Text ) -> Optional[Text]: """ Resolve the given mention to the name of the actual object. @@ -80,7 +80,7 @@ def resolve_mention( listed_items = tracker.get_slot(SLOT_LISTED_OBJECTS) last_object = tracker.get_slot(SLOT_LAST_OBJECT) last_object_type = tracker.get_slot(SLOT_LAST_OBJECT_TYPE) - current_object_type = tracker.get_slot(SLOT_OBJECT_TYPE) + current_object_type = object_type if not mention: return None @@ -142,6 +142,7 @@ def reset_attribute_slots( what attributes are detected by the NER. We take all attributes that are set, e.g. cuisine = Italian. If we don't reset the attribute slots after the request is done and the next utterance of the user would be, for example, "List all + is done and the next utterance of the user would be, for example, "List all restaurants that have wifi.", we would have two attribute slots set: "wifi" and "cuisine". Thus, we would filter all restaurants for two attributes now: wifi = True and cuisine = Italian. However, the user did not specify any @@ -162,3 +163,29 @@ def reset_attribute_slots( slots.append(SlotSet(attr, None)) return slots + + +async def get_object_type_dynamic( + tracker: "Tracker", + object_types: List, +) -> Optional[Text]: + """ + If the user ask a question about an attribute using an object name and + without specifying the object type, then this function searches the + corresponding object type. (e.g: when user asks'price range of B&B', this + function detects the object type as 'hotel') + + Args: + tracker: the tracker + object_types: list of object types in the knowledge base + + Returns: the name of the object type + """ + entities = tracker.latest_message["entities"] + entities_values = [entities[i]["entity"] for i in range(len(entities))] + for entity in entities_values: + if entity in object_types: + object_type_dynamic = entity + return object_type_dynamic + + return None From bfa920cdef31f262658ace8ec176cdd6b454c1a5 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 17 Jan 2023 09:00:41 +0530 Subject: [PATCH 04/94] updated changelog --- changelog/668.bugfix.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 changelog/668.bugfix.md diff --git a/changelog/668.bugfix.md b/changelog/668.bugfix.md new file mode 100644 index 000000000..e69de29bb From 1b1e4fdee954a2e41ca065f8c0f8b60da3113f03 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 17 Jan 2023 09:25:04 +0530 Subject: [PATCH 05/94] updated changelog --- changelog/668.bugfix.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog/668.bugfix.md b/changelog/668.bugfix.md index e69de29bb..093e8af5e 100644 --- a/changelog/668.bugfix.md +++ b/changelog/668.bugfix.md @@ -0,0 +1,2 @@ +In knowledge base actions, user can ask about an attribute of an object without specifying the object type by inferring the `object_type` using the entities. +Previously a user had to ask to list some options of a specific object type before asking about an attribute of an object. From 826803e1ad3035243a61758f120dd4fb4ccde579 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Mon, 6 Feb 2023 15:37:10 +0530 Subject: [PATCH 06/94] modified changelog name and content --- changelog/668.bugfix.md | 2 -- changelog/922.improvement.md | 31 +++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) delete mode 100644 changelog/668.bugfix.md create mode 100644 changelog/922.improvement.md diff --git a/changelog/668.bugfix.md b/changelog/668.bugfix.md deleted file mode 100644 index 093e8af5e..000000000 --- a/changelog/668.bugfix.md +++ /dev/null @@ -1,2 +0,0 @@ -In knowledge base actions, user can ask about an attribute of an object without specifying the object type by inferring the `object_type` using the entities. -Previously a user had to ask to list some options of a specific object type before asking about an attribute of an object. diff --git a/changelog/922.improvement.md b/changelog/922.improvement.md new file mode 100644 index 000000000..449db5853 --- /dev/null +++ b/changelog/922.improvement.md @@ -0,0 +1,31 @@ +## Problem +Rasa knowledge base actions cannot infer the object type of an object directly from the user message. This prevents action_query_knowledge from providing a suitable response when a user asks for a certain attribute of an object unless the user first asks to list objects related to that object type even though the knowledge base has the relevant information. That is, the knowledge base actions require the slot `object_type` to be set to one of the primary key values in the knowledge base for it to search through the objects. Here is an example: +``` +Your input -> what is the price range of Berlin Burrito Company? +Sorry, I'm not sure I understand. Can you rephrase? +Your input -> list some restaurants +Found the following objects of type 'restaurant': +1: Gong Gan +2: I due forni +3: Pfefferberg +4: Lụa Restaurant +5: Donath +Your input -> what is the price range of Berlin Burrito Company? +'Berlin Burrito Company' has the value 'cheap' for attribute 'price-range'. +``` + +## Proposed solution +- The improvement requires changes to the classes ActionQueryKnowledgeBase and InMemoryKnowledgeBase under rasa-sdk. +- The `object_type` can be inferred by utilizing the entity classification (DIET) where object types are used as entities to annotate object names. +This also requires changes to be made to slot management to enable dynamic inference of `object_type`. +- The scope of suggested solution is limited to user queries where they ask for an attribute of a given object without mentioning the object type and without having the need to first ask a list of options of corresponding object type. +- E.g: If the user asks ‘price range of Berlin Burrito Company’, then rasa will extract and set attribute slot value to ‘price-range’ and hotel slot value to ‘Berlin Burrito Company’. From this it can be inferred that the user is talking about object type ‘hotel’. + +## Summary of Changes +- In order to enable the inferance of `object_type` using the entities the following changes were made to existing code base: + - Extract the list of object_types from our knowledge base using a new function `get_object_types()` in `storage.py` under `InMemoryKnowledgeBase` calss. + - A new function named `get_object_type_dynamic` was added in `utils.py` to infer the object type of a given object using the entities and list of object types + - The relevant logic was added in `actions.py` to infer the object type using the above functionalities when object type slot is not set. + - To enable dynamic inference of `object_type`, changes to slot management is also required. Currently, the change made is to return None value for `object_type` slot. + + From 9cedec0f7c0ad4ad381c656c26c8195a1f72d06d Mon Sep 17 00:00:00 2001 From: Gajithra Date: Mon, 6 Feb 2023 15:49:32 +0530 Subject: [PATCH 07/94] uncommented code snippet that was previously commented to experiment the contribution changes --- rasa_sdk/knowledge_base/storage.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rasa_sdk/knowledge_base/storage.py b/rasa_sdk/knowledge_base/storage.py index eb2558ee8..4937d9da4 100644 --- a/rasa_sdk/knowledge_base/storage.py +++ b/rasa_sdk/knowledge_base/storage.py @@ -243,11 +243,11 @@ async def get_object( ) ) - # if not objects_of_interest or len(objects_of_interest) > 1: - # # TODO: - # # if multiple objects are found, the objects could be shown - # # to the user. the user then needs to clarify what object he meant. - # return None + if not objects_of_interest or len(objects_of_interest) > 1: + # TODO: + # if multiple objects are found, the objects could be shown + # to the user. the user then needs to clarify what object he meant. + return None return objects_of_interest[0] From ba3258193d846809e403dc1f297cc269045c3026 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Mon, 6 Feb 2023 15:54:27 +0530 Subject: [PATCH 08/94] added comprehensive typing hint --- rasa_sdk/knowledge_base/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_sdk/knowledge_base/utils.py b/rasa_sdk/knowledge_base/utils.py index 32c99de1c..2c7462941 100644 --- a/rasa_sdk/knowledge_base/utils.py +++ b/rasa_sdk/knowledge_base/utils.py @@ -167,7 +167,7 @@ def reset_attribute_slots( async def get_object_type_dynamic( tracker: "Tracker", - object_types: List, + object_types: List[Text], ) -> Optional[Text]: """ If the user ask a question about an attribute using an object name and From ca2f13b3a8a247f6e1911357e6c714753148a444 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Mon, 6 Feb 2023 16:01:54 +0530 Subject: [PATCH 09/94] renamed to --- rasa_sdk/knowledge_base/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_sdk/knowledge_base/utils.py b/rasa_sdk/knowledge_base/utils.py index 32c99de1c..7c355d99b 100644 --- a/rasa_sdk/knowledge_base/utils.py +++ b/rasa_sdk/knowledge_base/utils.py @@ -165,7 +165,7 @@ def reset_attribute_slots( return slots -async def get_object_type_dynamic( +async def match_extracted_entities_to_object_types( tracker: "Tracker", object_types: List, ) -> Optional[Text]: From cbb38dff5389d06a5657a8a7ddcc14509c83023a Mon Sep 17 00:00:00 2001 From: Gajithra Date: Mon, 6 Feb 2023 16:07:22 +0530 Subject: [PATCH 10/94] replaced the 'get_object_type_dynamic' function name with the new function name --- rasa_sdk/knowledge_base/actions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index a70d16e41..aef7c14ef 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -13,7 +13,7 @@ SLOT_LISTED_OBJECTS, get_object_name, get_attribute_slots, - get_object_type_dynamic, + match_extracted_entities_to_object_types, ) from rasa_sdk import utils from rasa_sdk.executor import CollectingDispatcher @@ -135,7 +135,7 @@ async def run( object_types = await utils.call_potential_coroutine( self.knowledge_base.get_object_types() ) - object_type_dynamic = await get_object_type_dynamic(tracker, object_types) + object_type_dynamic = await match_extracted_entities_to_object_types(tracker, object_types) if object_type: if not attribute or new_request: From e5f8d5685c08d0b80d1bce5fe40cddd437c21430 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Mon, 6 Feb 2023 16:08:33 +0530 Subject: [PATCH 11/94] replaced the 'get_object_type_dynamic' function name with the new function name --- changelog/922.improvement.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 changelog/922.improvement.md diff --git a/changelog/922.improvement.md b/changelog/922.improvement.md new file mode 100644 index 000000000..b0ee68034 --- /dev/null +++ b/changelog/922.improvement.md @@ -0,0 +1,31 @@ +## Problem +Rasa knowledge base actions cannot infer the object type of an object directly from the user message. This prevents action_query_knowledge from providing a suitable response when a user asks for a certain attribute of an object unless the user first asks to list objects related to that object type even though the knowledge base has the relevant information. That is, the knowledge base actions require the slot `object_type` to be set to one of the primary key values in the knowledge base for it to search through the objects. Here is an example: +``` +Your input -> what is the price range of Berlin Burrito Company? +Sorry, I'm not sure I understand. Can you rephrase? +Your input -> list some restaurants +Found the following objects of type 'restaurant': +1: Gong Gan +2: I due forni +3: Pfefferberg +4: Lụa Restaurant +5: Donath +Your input -> what is the price range of Berlin Burrito Company? +'Berlin Burrito Company' has the value 'cheap' for attribute 'price-range'. +``` + +## Proposed solution +- The improvement requires changes to the classes ActionQueryKnowledgeBase and InMemoryKnowledgeBase under rasa-sdk. +- The `object_type` can be inferred by utilizing the entity classification (DIET) where object types are used as entities to annotate object names. +This also requires changes to be made to slot management to enable dynamic inference of `object_type`. +- The scope of suggested solution is limited to user queries where they ask for an attribute of a given object without mentioning the object type and without having the need to first ask a list of options of corresponding object type. +- E.g: If the user asks ‘price range of Berlin Burrito Company’, then rasa will extract and set attribute slot value to ‘price-range’ and hotel slot value to ‘Berlin Burrito Company’. From this it can be inferred that the user is talking about object type ‘hotel’. + +## Summary of Changes +- In order to enable the inferance of `object_type` using the entities the following changes were made to existing code base: + - Extract the list of object_types from our knowledge base using a new function `get_object_types()` in `storage.py` under `InMemoryKnowledgeBase` calss. + - A new function named `match_extracted_entities_to_object_types` was added in `utils.py` to infer the object type of a given object using the entities and list of object types + - The relevant logic was added in `actions.py` to infer the object type using the above functionalities when object type slot is not set. + - To enable dynamic inference of `object_type`, changes to slot management is also required. Currently, the change made is to return None value for `object_type` slot. + + From 0644efb93b18fcc857c59ce036ea72e96701894f Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 7 Feb 2023 22:30:43 +0530 Subject: [PATCH 12/94] modified sentences based on PR review comments --- changelog/922.improvement.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/changelog/922.improvement.md b/changelog/922.improvement.md index 449db5853..e4a9cce61 100644 --- a/changelog/922.improvement.md +++ b/changelog/922.improvement.md @@ -1,5 +1,5 @@ ## Problem -Rasa knowledge base actions cannot infer the object type of an object directly from the user message. This prevents action_query_knowledge from providing a suitable response when a user asks for a certain attribute of an object unless the user first asks to list objects related to that object type even though the knowledge base has the relevant information. That is, the knowledge base actions require the slot `object_type` to be set to one of the primary key values in the knowledge base for it to search through the objects. Here is an example: +Rasa knowledge base actions requires the `object_type` slot to be set before it can query information for a given object belonging to that object type. This prevents action_query_knowledge from providing a expected response when a user asks for a certain attribute of an object unless the user first asks to list objects related to that object type even though the knowledge base has the relevant information. That is, the knowledge base actions require the slot `object_type` to be set to one of the primary key values in the knowledge base for it to search through the objects. Here is an example: ``` Your input -> what is the price range of Berlin Burrito Company? Sorry, I'm not sure I understand. Can you rephrase? @@ -13,16 +13,17 @@ Found the following objects of type 'restaurant': Your input -> what is the price range of Berlin Burrito Company? 'Berlin Burrito Company' has the value 'cheap' for attribute 'price-range'. ``` +From this example, it is clear that the knowledge base has the required the information that the user wants. However, it cannot directly infer the object_type from the object name which places a requirement on the user to first ask the chatbot to list some objects of a given object_type. ## Proposed solution - The improvement requires changes to the classes ActionQueryKnowledgeBase and InMemoryKnowledgeBase under rasa-sdk. -- The `object_type` can be inferred by utilizing the entity classification (DIET) where object types are used as entities to annotate object names. +- The `object_type` can be inferred by utilizing the entity extraction model(DIET) where object types are used as entities to annotate object names. This also requires changes to be made to slot management to enable dynamic inference of `object_type`. - The scope of suggested solution is limited to user queries where they ask for an attribute of a given object without mentioning the object type and without having the need to first ask a list of options of corresponding object type. -- E.g: If the user asks ‘price range of Berlin Burrito Company’, then rasa will extract and set attribute slot value to ‘price-range’ and hotel slot value to ‘Berlin Burrito Company’. From this it can be inferred that the user is talking about object type ‘hotel’. +- E.g: If the user asks ‘price range of Berlin Burrito Company’, then rasa knowledge base query function will extract and set attribute slot value to ‘price-range’ and hotel slot value to ‘Berlin Burrito Company’. From this it can be inferred that the user is talking about object type ‘hotel’. ## Summary of Changes -- In order to enable the inferance of `object_type` using the entities the following changes were made to existing code base: +- In order to enable the inference of `object_type` using the entities the following changes were made to existing code base: - Extract the list of object_types from our knowledge base using a new function `get_object_types()` in `storage.py` under `InMemoryKnowledgeBase` calss. - A new function named `get_object_type_dynamic` was added in `utils.py` to infer the object type of a given object using the entities and list of object types - The relevant logic was added in `actions.py` to infer the object type using the above functionalities when object type slot is not set. From 4e4e24bc765d4b7412410ef80d2597da3c50325f Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 7 Feb 2023 22:33:11 +0530 Subject: [PATCH 13/94] updated the 'get_object_type_dynamic' with new name --- changelog/922.improvement.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/922.improvement.md b/changelog/922.improvement.md index e4a9cce61..9429ec4ea 100644 --- a/changelog/922.improvement.md +++ b/changelog/922.improvement.md @@ -25,7 +25,7 @@ This also requires changes to be made to slot management to enable dynamic infer ## Summary of Changes - In order to enable the inference of `object_type` using the entities the following changes were made to existing code base: - Extract the list of object_types from our knowledge base using a new function `get_object_types()` in `storage.py` under `InMemoryKnowledgeBase` calss. - - A new function named `get_object_type_dynamic` was added in `utils.py` to infer the object type of a given object using the entities and list of object types + - A new function named `match_extracted_entities_to_object_types` was added in `utils.py` to infer the object type of a given object using the entities and list of object types - The relevant logic was added in `actions.py` to infer the object type using the above functionalities when object type slot is not set. - To enable dynamic inference of `object_type`, changes to slot management is also required. Currently, the change made is to return None value for `object_type` slot. From 34dea767cfafcb606a0c473c8fdc7264845529cb Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 7 Feb 2023 22:36:17 +0530 Subject: [PATCH 14/94] directly return entity without assigning it to an unused variable as suggested in PR review --- rasa_sdk/knowledge_base/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rasa_sdk/knowledge_base/utils.py b/rasa_sdk/knowledge_base/utils.py index 2c7462941..d80333a39 100644 --- a/rasa_sdk/knowledge_base/utils.py +++ b/rasa_sdk/knowledge_base/utils.py @@ -185,7 +185,6 @@ async def get_object_type_dynamic( entities_values = [entities[i]["entity"] for i in range(len(entities))] for entity in entities_values: if entity in object_types: - object_type_dynamic = entity - return object_type_dynamic + return entity return None From 22d7098f23f8052d56bd870cf789c75222c0a295 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 7 Feb 2023 22:37:47 +0530 Subject: [PATCH 15/94] modified line 184 to safeguard against KeyErrors as suggested in PR review --- rasa_sdk/knowledge_base/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_sdk/knowledge_base/utils.py b/rasa_sdk/knowledge_base/utils.py index d80333a39..87876690e 100644 --- a/rasa_sdk/knowledge_base/utils.py +++ b/rasa_sdk/knowledge_base/utils.py @@ -181,7 +181,7 @@ async def get_object_type_dynamic( Returns: the name of the object type """ - entities = tracker.latest_message["entities"] + entities = tracker.latest_message.get("entities", []) entities_values = [entities[i]["entity"] for i in range(len(entities))] for entity in entities_values: if entity in object_types: From a77350b01f4fddab12c2408850aa1cbc569926ea Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 7 Feb 2023 22:42:29 +0530 Subject: [PATCH 16/94] added suggestion to code change in line 185 as mentioned in the PR --- rasa_sdk/knowledge_base/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rasa_sdk/knowledge_base/utils.py b/rasa_sdk/knowledge_base/utils.py index 87876690e..206e0f40e 100644 --- a/rasa_sdk/knowledge_base/utils.py +++ b/rasa_sdk/knowledge_base/utils.py @@ -182,7 +182,9 @@ async def get_object_type_dynamic( Returns: the name of the object type """ entities = tracker.latest_message.get("entities", []) - entities_values = [entities[i]["entity"] for i in range(len(entities))] + # entities_values = [entities[i]["entity"] for i in range(len(entities))] + entities_values = [entity.get("entity") for entity in entities] + print(entities_vaues) for entity in entities_values: if entity in object_types: return entity From 7e6352c79e361c4f7976b207e8b5fedd0f12b8b2 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 7 Feb 2023 22:43:13 +0530 Subject: [PATCH 17/94] added suggestion to code change in line 185 as mentioned in the PR --- rasa_sdk/knowledge_base/utils.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/rasa_sdk/knowledge_base/utils.py b/rasa_sdk/knowledge_base/utils.py index 206e0f40e..bc825bd04 100644 --- a/rasa_sdk/knowledge_base/utils.py +++ b/rasa_sdk/knowledge_base/utils.py @@ -182,9 +182,7 @@ async def get_object_type_dynamic( Returns: the name of the object type """ entities = tracker.latest_message.get("entities", []) - # entities_values = [entities[i]["entity"] for i in range(len(entities))] entities_values = [entity.get("entity") for entity in entities] - print(entities_vaues) for entity in entities_values: if entity in object_types: return entity From 1658e99a10d3f9328675bc46acffa9dbe622bf02 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Thu, 9 Feb 2023 13:20:22 +0530 Subject: [PATCH 18/94] modified after running grammerly --- changelog/922.improvement.md | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/changelog/922.improvement.md b/changelog/922.improvement.md index 9429ec4ea..05899102f 100644 --- a/changelog/922.improvement.md +++ b/changelog/922.improvement.md @@ -1,5 +1,5 @@ ## Problem -Rasa knowledge base actions requires the `object_type` slot to be set before it can query information for a given object belonging to that object type. This prevents action_query_knowledge from providing a expected response when a user asks for a certain attribute of an object unless the user first asks to list objects related to that object type even though the knowledge base has the relevant information. That is, the knowledge base actions require the slot `object_type` to be set to one of the primary key values in the knowledge base for it to search through the objects. Here is an example: +Rasa knowledge base actions require the `object_type` slot to be set before it can query information for a given object of that object type. This prevents action_query_knowledge from providing an expected response when a user asks for a certain attribute of an object unless the user first asks to list objects related to that object type even though the knowledge base has the relevant information. That is, the knowledge base actions require the slot `object_type` to be set to one of the primary key values in the knowledge base for it to search through the objects. Here is an example: ``` Your input -> what is the price range of Berlin Burrito Company? Sorry, I'm not sure I understand. Can you rephrase? @@ -11,22 +11,20 @@ Found the following objects of type 'restaurant': 4: Lụa Restaurant 5: Donath Your input -> what is the price range of Berlin Burrito Company? -'Berlin Burrito Company' has the value 'cheap' for attribute 'price-range'. +'Berlin Burrito Company' has the value 'cheap' for the attribute 'price-range'. ``` -From this example, it is clear that the knowledge base has the required the information that the user wants. However, it cannot directly infer the object_type from the object name which places a requirement on the user to first ask the chatbot to list some objects of a given object_type. +From this example, it is clear that the knowledge base has the required information that the user wants. However, it cannot directly infer the object_type from the object name which places a requirement on the user to ask the chatbot to list some objects of a given object_type first. ## Proposed solution - The improvement requires changes to the classes ActionQueryKnowledgeBase and InMemoryKnowledgeBase under rasa-sdk. -- The `object_type` can be inferred by utilizing the entity extraction model(DIET) where object types are used as entities to annotate object names. -This also requires changes to be made to slot management to enable dynamic inference of `object_type`. -- The scope of suggested solution is limited to user queries where they ask for an attribute of a given object without mentioning the object type and without having the need to first ask a list of options of corresponding object type. -- E.g: If the user asks ‘price range of Berlin Burrito Company’, then rasa knowledge base query function will extract and set attribute slot value to ‘price-range’ and hotel slot value to ‘Berlin Burrito Company’. From this it can be inferred that the user is talking about object type ‘hotel’. +- The `object_type` can be inferred by utilizing the entity extraction model(DIET) where object types are used as entities to annotate object names. +This also requires changes to slot management to enable dynamic inference of `object_type`. +- The scope of the suggested solution is limited to user queries where they ask for an attribute of a given object without mentioning the object type and without needing to ask for a list of options of the corresponding object type first. +- For E.g: If the user asks for the ‘price range of Berlin Burrito Company’, then the rasa knowledge base query function will extract and set the attribute slot value to ‘price-range’ and hotel slot value to ‘Berlin Burrito Company’. From this, it can be inferred that the user is talking about the object type ‘hotel’. ## Summary of Changes -- In order to enable the inference of `object_type` using the entities the following changes were made to existing code base: - - Extract the list of object_types from our knowledge base using a new function `get_object_types()` in `storage.py` under `InMemoryKnowledgeBase` calss. +- Following changes were made on the existing code to enable the inference of `object_type` using the entities: + - Extract the list of object_types from our knowledge base using a new function `get_object_types()` in `storage.py` under the `InMemoryKnowledgeBase` class. - A new function named `match_extracted_entities_to_object_types` was added in `utils.py` to infer the object type of a given object using the entities and list of object types - - The relevant logic was added in `actions.py` to infer the object type using the above functionalities when object type slot is not set. - - To enable dynamic inference of `object_type`, changes to slot management is also required. Currently, the change made is to return None value for `object_type` slot. - - + - The relevant logic was added in `actions.py` to infer the object type using the above functionalities when the object type slot is None. + - To enable dynamic inference of `object_type`, changes to slot management are also required. Currently, the functions return a None value for the `object_type` slot. \ No newline at end of file From 85b022b0909f1dfaea54ea856e0bedc04a23526a Mon Sep 17 00:00:00 2001 From: Gajithra Date: Mon, 13 Feb 2023 15:45:27 +0530 Subject: [PATCH 19/94] Added TrackerKnowledgeBase class in tracker.py to set object type slot --- rasa_sdk/knowledge_base/tracker.py | 31 ++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 rasa_sdk/knowledge_base/tracker.py diff --git a/rasa_sdk/knowledge_base/tracker.py b/rasa_sdk/knowledge_base/tracker.py new file mode 100644 index 000000000..afa548ce6 --- /dev/null +++ b/rasa_sdk/knowledge_base/tracker.py @@ -0,0 +1,31 @@ +from rasa_sdk.interfaces import Tracker +import typing +from typing import List, Text + +from rasa_sdk.events import EventType + +if typing.TYPE_CHECKING: # pragma: no cover + from rasa_sdk.executor import CollectingDispatcher + from rasa_sdk.types import DomainDict, TrackerState + + +class MyTracker(Tracker): + def __init__( + self, + tracker: Tracker, + ) -> None: + self.slots = tracker.slots + self.events = tracker.events + + def add_object_type_slot(self, slots: List[EventType], object_type_value:Text) -> None: + """Adds slots to the current tracker. + + Args: + slots: `SlotSet` events. + """ + for event in slots: + if not event.get("event") == "slot": + continue + print(f"event: {event}") + self.slots[event["name"]] = object_type_value + self.events.append(event) \ No newline at end of file From ac01e97bebee49317bb3f0a785ef10c898db1897 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Mon, 13 Feb 2023 23:34:20 +0530 Subject: [PATCH 20/94] modified class name --- rasa_sdk/knowledge_base/tracker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_sdk/knowledge_base/tracker.py b/rasa_sdk/knowledge_base/tracker.py index afa548ce6..e104a78b1 100644 --- a/rasa_sdk/knowledge_base/tracker.py +++ b/rasa_sdk/knowledge_base/tracker.py @@ -9,7 +9,7 @@ from rasa_sdk.types import DomainDict, TrackerState -class MyTracker(Tracker): +class TrackerKnowledgeBase(Tracker): def __init__( self, tracker: Tracker, From f781e24cbae827cd661d91151d491efcbd3ef6be Mon Sep 17 00:00:00 2001 From: Gajithra Date: Mon, 13 Feb 2023 23:35:08 +0530 Subject: [PATCH 21/94] removed passing object_type value as an argument in functions --- rasa_sdk/knowledge_base/utils.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/rasa_sdk/knowledge_base/utils.py b/rasa_sdk/knowledge_base/utils.py index 2c7462941..02b6aecc0 100644 --- a/rasa_sdk/knowledge_base/utils.py +++ b/rasa_sdk/knowledge_base/utils.py @@ -15,7 +15,6 @@ def get_object_name( tracker: "Tracker", - object_type: Text, ordinal_mention_mapping: Dict[Text, Callable], use_last_object_mention: bool = True, ) -> Optional[Text]: @@ -35,10 +34,11 @@ def get_object_name( knowledge base) """ mention = tracker.get_slot(SLOT_MENTION) + object_type = tracker.get_slot(SLOT_OBJECT_TYPE) # the user referred to the object by a mention, such as "first one" if mention: - return resolve_mention(tracker, ordinal_mention_mapping, object_type) + return resolve_mention(tracker, ordinal_mention_mapping) # check whether the user referred to the objet by its name object_name = tracker.get_slot(object_type) @@ -54,7 +54,7 @@ def get_object_name( def resolve_mention( - tracker: "Tracker", ordinal_mention_mapping: Dict[Text, Callable], object_type: Text + tracker: "Tracker", ordinal_mention_mapping: Dict[Text, Callable] ) -> Optional[Text]: """ Resolve the given mention to the name of the actual object. @@ -80,7 +80,7 @@ def resolve_mention( listed_items = tracker.get_slot(SLOT_LISTED_OBJECTS) last_object = tracker.get_slot(SLOT_LAST_OBJECT) last_object_type = tracker.get_slot(SLOT_LAST_OBJECT_TYPE) - current_object_type = object_type + current_object_type = tracker.get_slot(SLOT_OBJECT_TYPE) if not mention: return None @@ -92,8 +92,10 @@ def resolve_mention( # NOTE: # for now we just assume that if the user refers to an object, for # example via "it" or "that restaurant", they are actually referring to the last - # object that was detected. - if current_object_type == last_object_type: + # object that was detected. + # Since object type slot is reset to 'None' value, it is sufficient to only check + # whether the last_object_type is not None. + if last_object_type: return last_object return None From 13e21f6e7c350d36553ce241abdc3f5d0dc1f5ec Mon Sep 17 00:00:00 2001 From: Gajithra Date: Mon, 13 Feb 2023 23:36:33 +0530 Subject: [PATCH 22/94] imported TrackerKnowledgeBase class | modified function based on changes done in utils.py | assigned extracted object type to object_type variable --- rasa_sdk/knowledge_base/actions.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index a70d16e41..b71a85753 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -19,6 +19,7 @@ from rasa_sdk.executor import CollectingDispatcher from rasa_sdk.interfaces import Tracker from rasa_sdk.knowledge_base.storage import KnowledgeBase +from rasa_sdk.knowledge_base.tracker import TrackerKnowledgeBase if typing.TYPE_CHECKING: # pragma: no cover from rasa_sdk.types import DomainDict @@ -135,7 +136,10 @@ async def run( object_types = await utils.call_potential_coroutine( self.knowledge_base.get_object_types() ) - object_type_dynamic = await get_object_type_dynamic(tracker, object_types) + object_type = await get_object_type_dynamic(tracker, object_types) + set_object_type_slot_event = [SlotSet(SLOT_OBJECT_TYPE, object_type)] + my_tracker = TrackerKnowledgeBase(tracker) + my_tracker.add_object_type_slot(set_object_type_slot_event, object_type) if object_type: if not attribute or new_request: @@ -146,11 +150,6 @@ async def run( dispatcher, object_type, attribute, tracker ) - if object_type_dynamic and attribute: - return await self._query_attribute( - dispatcher, object_type_dynamic, attribute, tracker - ) - if last_object_type and has_mention and attribute: return await self._query_attribute( dispatcher, last_object_type, attribute, tracker @@ -227,7 +226,6 @@ async def _query_attribute( """ object_name = get_object_name( tracker, - object_type, self.knowledge_base.ordinal_mention_mapping, self.use_last_object_mention, ) From 893c4506803a0773f4549d1d3bffe686a15eb2c6 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 28 Feb 2023 12:01:54 +0530 Subject: [PATCH 23/94] removed get_object_types from KnowledgeBase class | removed async from get_object_types --- rasa_sdk/knowledge_base/storage.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/rasa_sdk/knowledge_base/storage.py b/rasa_sdk/knowledge_base/storage.py index 4937d9da4..4f6ab3466 100644 --- a/rasa_sdk/knowledge_base/storage.py +++ b/rasa_sdk/knowledge_base/storage.py @@ -110,15 +110,6 @@ async def get_object( """ raise NotImplementedError("Method is not implemented.") - async def get_object_types(self) -> List[Text]: - """ - Returns a list of all object types that belong to the knowledge base. - - Returns: list of object types - """ - raise NotImplementedError("Method is not implemented.") - - class InMemoryKnowledgeBase(KnowledgeBase): def __init__(self, data_file: Text) -> None: """ @@ -251,5 +242,5 @@ async def get_object( return objects_of_interest[0] - async def get_object_types(self) -> List[Text]: + def get_object_types(self) -> List[Text]: return list(self.data.keys()) From 2374ab663503c1bd1ecaf0b1cb9c23ed636772bf Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 28 Feb 2023 12:03:47 +0530 Subject: [PATCH 24/94] removed await in line 136 after convering get_object_types function from async to normal --- rasa_sdk/knowledge_base/actions.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 14bd57a03..19e2363e9 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -133,9 +133,7 @@ async def run( if not object_type: # sets the object type dynamically from entities if object_type is not found # in user query - object_types = await utils.call_potential_coroutine( - self.knowledge_base.get_object_types() - ) + object_types = self.knowledge_base.get_object_types() object_type = await match_extracted_entities_to_object_types(tracker, object_types) set_object_type_slot_event = [SlotSet(SLOT_OBJECT_TYPE, object_type)] my_tracker = TrackerKnowledgeBase(tracker) From 92769dd0167403aba3c4b5107cd548efd885d030 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 28 Feb 2023 12:09:03 +0530 Subject: [PATCH 25/94] modified code to set the object type slot temporarily using the method in Tracker class --- rasa_sdk/knowledge_base/actions.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 19e2363e9..c5a36fd08 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -136,8 +136,7 @@ async def run( object_types = self.knowledge_base.get_object_types() object_type = await match_extracted_entities_to_object_types(tracker, object_types) set_object_type_slot_event = [SlotSet(SLOT_OBJECT_TYPE, object_type)] - my_tracker = TrackerKnowledgeBase(tracker) - my_tracker.add_object_type_slot(set_object_type_slot_event, object_type) + tracker.add_slots(set_object_type_slot_event) # temporarily set the `object_type_slot` to extracted value if object_type: if not attribute or new_request: From adfb4f41dd2ef187e9c557351d100409fb27e242 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 28 Feb 2023 12:09:38 +0530 Subject: [PATCH 26/94] removing tracker.py which contained a method to temporarily set the object type slot --- rasa_sdk/knowledge_base/tracker.py | 31 ------------------------------ 1 file changed, 31 deletions(-) delete mode 100644 rasa_sdk/knowledge_base/tracker.py diff --git a/rasa_sdk/knowledge_base/tracker.py b/rasa_sdk/knowledge_base/tracker.py deleted file mode 100644 index e104a78b1..000000000 --- a/rasa_sdk/knowledge_base/tracker.py +++ /dev/null @@ -1,31 +0,0 @@ -from rasa_sdk.interfaces import Tracker -import typing -from typing import List, Text - -from rasa_sdk.events import EventType - -if typing.TYPE_CHECKING: # pragma: no cover - from rasa_sdk.executor import CollectingDispatcher - from rasa_sdk.types import DomainDict, TrackerState - - -class TrackerKnowledgeBase(Tracker): - def __init__( - self, - tracker: Tracker, - ) -> None: - self.slots = tracker.slots - self.events = tracker.events - - def add_object_type_slot(self, slots: List[EventType], object_type_value:Text) -> None: - """Adds slots to the current tracker. - - Args: - slots: `SlotSet` events. - """ - for event in slots: - if not event.get("event") == "slot": - continue - print(f"event: {event}") - self.slots[event["name"]] = object_type_value - self.events.append(event) \ No newline at end of file From c77c4b18042d5418fea16e578644c1c5ee6d77d7 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 28 Feb 2023 12:10:06 +0530 Subject: [PATCH 27/94] removed the import from tracker.py --- rasa_sdk/knowledge_base/actions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index c5a36fd08..d6002c6db 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -19,7 +19,6 @@ from rasa_sdk.executor import CollectingDispatcher from rasa_sdk.interfaces import Tracker from rasa_sdk.knowledge_base.storage import KnowledgeBase -from rasa_sdk.knowledge_base.tracker import TrackerKnowledgeBase if typing.TYPE_CHECKING: # pragma: no cover from rasa_sdk.types import DomainDict From a05abd5e49ee9c7c824b56e33b0ade41d91fc0ed Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 28 Feb 2023 12:32:24 +0530 Subject: [PATCH 28/94] removed code that gets current_object_type from the slot --- rasa_sdk/knowledge_base/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rasa_sdk/knowledge_base/utils.py b/rasa_sdk/knowledge_base/utils.py index 05031ae3f..269eabe73 100644 --- a/rasa_sdk/knowledge_base/utils.py +++ b/rasa_sdk/knowledge_base/utils.py @@ -80,7 +80,6 @@ def resolve_mention( listed_items = tracker.get_slot(SLOT_LISTED_OBJECTS) last_object = tracker.get_slot(SLOT_LAST_OBJECT) last_object_type = tracker.get_slot(SLOT_LAST_OBJECT_TYPE) - current_object_type = tracker.get_slot(SLOT_OBJECT_TYPE) if not mention: return None From 206b4193145d5a029f4bfbc32f76bb232d7990f5 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 28 Feb 2023 12:33:37 +0530 Subject: [PATCH 29/94] converted match_extracted_entities_to_object_types function from async to normal --- rasa_sdk/knowledge_base/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_sdk/knowledge_base/utils.py b/rasa_sdk/knowledge_base/utils.py index 269eabe73..85022cbe4 100644 --- a/rasa_sdk/knowledge_base/utils.py +++ b/rasa_sdk/knowledge_base/utils.py @@ -166,7 +166,7 @@ def reset_attribute_slots( return slots -async def match_extracted_entities_to_object_types( +def match_extracted_entities_to_object_types( tracker: "Tracker", object_types: List[Text], ) -> Optional[Text]: From 6b8082d86635c56deaa0005aee72a74b936d62c7 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 28 Feb 2023 12:34:11 +0530 Subject: [PATCH 30/94] removed await for match_extracted_entities_to_object_types function --- rasa_sdk/knowledge_base/actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index d6002c6db..226594652 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -133,7 +133,7 @@ async def run( # sets the object type dynamically from entities if object_type is not found # in user query object_types = self.knowledge_base.get_object_types() - object_type = await match_extracted_entities_to_object_types(tracker, object_types) + object_type = match_extracted_entities_to_object_types(tracker, object_types) set_object_type_slot_event = [SlotSet(SLOT_OBJECT_TYPE, object_type)] tracker.add_slots(set_object_type_slot_event) # temporarily set the `object_type_slot` to extracted value From 7656a3fc56576baf5151fb2aa4af4c67ad27e718 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 28 Feb 2023 12:44:26 +0530 Subject: [PATCH 31/94] modified action logic after assigning extracted object type to an object_type variable --- rasa_sdk/knowledge_base/actions.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 226594652..9ecbbbaec 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -136,15 +136,13 @@ async def run( object_type = match_extracted_entities_to_object_types(tracker, object_types) set_object_type_slot_event = [SlotSet(SLOT_OBJECT_TYPE, object_type)] tracker.add_slots(set_object_type_slot_event) # temporarily set the `object_type_slot` to extracted value - - if object_type: - if not attribute or new_request: - return await self._query_objects(dispatcher, object_type, tracker) - - elif attribute: - return await self._query_attribute( - dispatcher, object_type, attribute, tracker - ) + + if object_type and attribute: + return await self._query_attribute( + dispatcher, object_type, attribute, tracker + ) + elif (object_type and not attribute) or (object_type and new_request): + return await self._query_objects(dispatcher, object_type, tracker) if last_object_type and has_mention and attribute: return await self._query_attribute( From f79ed4b11e4a55b860bdac3efbde8d0474811d53 Mon Sep 17 00:00:00 2001 From: Dilanka96 Date: Wed, 1 Mar 2023 14:56:52 +0530 Subject: [PATCH 32/94] Unit test functions --- tests/knowledge_base/test_actions.py | 61 +++++++++++++++++++++------- tests/knowledge_base/test_utils.py | 48 ++++++++++++++++++++++ 2 files changed, 95 insertions(+), 14 deletions(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index 6ae808348..60985db30 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -28,9 +28,10 @@ def compare_slots(slot_list_1, slot_list_2): @pytest.mark.parametrize( - "slots,expected_slots", + "latest_message,slots,expected_slots", [ - ( + ( + {}, { SLOT_MENTION: None, SLOT_ATTRIBUTE: None, @@ -42,37 +43,51 @@ def compare_slots(slot_list_1, slot_list_2): [ SlotSet(SLOT_MENTION, None), SlotSet(SLOT_ATTRIBUTE, None), - SlotSet(SLOT_OBJECT_TYPE, "restaurant"), + SlotSet(SLOT_OBJECT_TYPE, None), SlotSet(SLOT_LAST_OBJECT, None), SlotSet(SLOT_LAST_OBJECT_TYPE, "restaurant"), SlotSet(SLOT_LISTED_OBJECTS, [3, 2, 1]), ], ), ( + {}, { SLOT_MENTION: None, SLOT_ATTRIBUTE: None, SLOT_OBJECT_TYPE: "restaurant", SLOT_LISTED_OBJECTS: None, SLOT_LAST_OBJECT: None, - SLOT_LAST_OBJECT_TYPE: "restaurant", + SLOT_LAST_OBJECT_TYPE: None, "cuisine": "Italian", }, [ SlotSet(SLOT_MENTION, None), SlotSet(SLOT_ATTRIBUTE, None), - SlotSet(SLOT_OBJECT_TYPE, "restaurant"), + SlotSet(SLOT_OBJECT_TYPE, None), SlotSet(SLOT_LAST_OBJECT, None), SlotSet(SLOT_LAST_OBJECT_TYPE, "restaurant"), SlotSet(SLOT_LISTED_OBJECTS, [3, 1]), SlotSet("cuisine", None), ], ), - ( + ( + { + + "entities":[ + { + "entity":"attribute", + + }, + { + "entity":"mention", + } + ], + + }, { SLOT_MENTION: "2", SLOT_ATTRIBUTE: "cuisine", - SLOT_OBJECT_TYPE: "restaurant", + SLOT_OBJECT_TYPE: None, SLOT_LISTED_OBJECTS: [1, 2, 3], SLOT_LAST_OBJECT: None, SLOT_LAST_OBJECT_TYPE: "restaurant", @@ -80,16 +95,29 @@ def compare_slots(slot_list_1, slot_list_2): [ SlotSet(SLOT_MENTION, None), SlotSet(SLOT_ATTRIBUTE, None), - SlotSet(SLOT_OBJECT_TYPE, "restaurant"), + SlotSet(SLOT_OBJECT_TYPE, None), SlotSet(SLOT_LAST_OBJECT, 2), SlotSet(SLOT_LAST_OBJECT_TYPE, "restaurant"), ], ), ( + { + + "entities":[ + { + "entity":"attribute", + + }, + { + "entity":"restaurant", + } + ], + + }, { SLOT_MENTION: None, SLOT_ATTRIBUTE: "cuisine", - SLOT_OBJECT_TYPE: "restaurant", + SLOT_OBJECT_TYPE: None, SLOT_LISTED_OBJECTS: [1, 2, 3], SLOT_LAST_OBJECT: None, SLOT_LAST_OBJECT_TYPE: "restaurant", @@ -98,12 +126,17 @@ def compare_slots(slot_list_1, slot_list_2): [ SlotSet(SLOT_MENTION, None), SlotSet(SLOT_ATTRIBUTE, None), - SlotSet(SLOT_OBJECT_TYPE, "restaurant"), + SlotSet(SLOT_OBJECT_TYPE, None), SlotSet(SLOT_LAST_OBJECT, 1), SlotSet(SLOT_LAST_OBJECT_TYPE, "restaurant"), ], ), ( + { + + "entities":[], + + }, { SLOT_MENTION: None, SLOT_ATTRIBUTE: None, @@ -116,15 +149,15 @@ def compare_slots(slot_list_1, slot_list_2): ), ], ) -async def test_action_run(data_file, slots, expected_slots): +async def test_action_run(data_file, latest_message,slots, expected_slots): knowledge_base = InMemoryKnowledgeBase(data_file) action = ActionQueryKnowledgeBase(knowledge_base) dispatcher = CollectingDispatcher() - tracker = Tracker("default", slots, {}, [], False, None, {}, "action_listen") - + + tracker = Tracker("default", slots, latest_message, [], False, None, {}, "action_listen") actual_slots = await action.run(dispatcher, tracker, {}) - + print(f"actual slots: {actual_slots}\nexpected slots:{expected_slots}") compare_slots(expected_slots, actual_slots) compare_slots(actual_slots, expected_slots) diff --git a/tests/knowledge_base/test_utils.py b/tests/knowledge_base/test_utils.py index 92cc819b4..7e350fd75 100644 --- a/tests/knowledge_base/test_utils.py +++ b/tests/knowledge_base/test_utils.py @@ -3,6 +3,7 @@ from rasa_sdk import Tracker from rasa_sdk.events import SlotSet from rasa_sdk.knowledge_base.utils import ( + match_extracted_entities_to_object_types, get_attribute_slots, reset_attribute_slots, get_object_name, @@ -129,3 +130,50 @@ def test_get_object_name(slots, use_last_object_mention, expected_object_name): ) assert actual_object_name == expected_object_name + + + +@pytest.mark.parametrize( + "latest_message,object_types,expected_object_name", + [ + ( + { + "entities":[ + { + "entity":"attribute", + "start":0, + "end":11, + "confidence_entity":0.9997496008872986, + "value":"price-range", + "extractor":"DIETClassifier", + "processors":[ + "EntitySynonymMapper" + ] + }, + { + "entity":"restaurant", + "start":15, + "end":21, + "confidence_entity":0.9953670501708984, + "value":"Donath", + "extractor":"DIETClassifier" + } + ], + + }, + ['hotel', 'restaurant'], + 'restaurant' + + ), + + ] +) +def test_match_extracted_entities_to_object_types(latest_message, object_types, expected_object_name): + + tracker = Tracker("default", {}, latest_message, [], False, None, {}, "action_listen") + actual_object_name = match_extracted_entities_to_object_types( + tracker,object_types + ) + print(actual_object_name , expected_object_name) + + assert actual_object_name == expected_object_name \ No newline at end of file From 27adc467a7e77bf53cc1f3ff62efc926f41865e6 Mon Sep 17 00:00:00 2001 From: Dilanka96 Date: Wed, 1 Mar 2023 15:28:39 +0530 Subject: [PATCH 33/94] Formatted with black --- rasa_sdk/executor.py | 1 - rasa_sdk/knowledge_base/actions.py | 10 +++-- rasa_sdk/knowledge_base/storage.py | 4 +- rasa_sdk/knowledge_base/utils.py | 2 +- scripts/release.py | 7 ++-- tests/knowledge_base/test_actions.py | 43 +++++++++------------ tests/knowledge_base/test_utils.py | 57 +++++++++++++--------------- 7 files changed, 57 insertions(+), 67 deletions(-) diff --git a/rasa_sdk/executor.py b/rasa_sdk/executor.py index b1377fe15..341d33515 100644 --- a/rasa_sdk/executor.py +++ b/rasa_sdk/executor.py @@ -24,7 +24,6 @@ class CollectingDispatcher: """Send messages back to user""" def __init__(self) -> None: - self.messages: List[Dict[Text, Any]] = [] def utter_message( diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 9ecbbbaec..6684a969d 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -133,10 +133,14 @@ async def run( # sets the object type dynamically from entities if object_type is not found # in user query object_types = self.knowledge_base.get_object_types() - object_type = match_extracted_entities_to_object_types(tracker, object_types) + object_type = match_extracted_entities_to_object_types( + tracker, object_types + ) set_object_type_slot_event = [SlotSet(SLOT_OBJECT_TYPE, object_type)] - tracker.add_slots(set_object_type_slot_event) # temporarily set the `object_type_slot` to extracted value - + tracker.add_slots( + set_object_type_slot_event + ) # temporarily set the `object_type_slot` to extracted value + if object_type and attribute: return await self._query_attribute( dispatcher, object_type, attribute, tracker diff --git a/rasa_sdk/knowledge_base/storage.py b/rasa_sdk/knowledge_base/storage.py index 4f6ab3466..16e222719 100644 --- a/rasa_sdk/knowledge_base/storage.py +++ b/rasa_sdk/knowledge_base/storage.py @@ -12,7 +12,6 @@ class KnowledgeBase: def __init__(self) -> None: - self.ordinal_mention_mapping = { "1": lambda l: l[0], "2": lambda l: l[1], @@ -110,6 +109,7 @@ async def get_object( """ raise NotImplementedError("Method is not implemented.") + class InMemoryKnowledgeBase(KnowledgeBase): def __init__(self, data_file: Text) -> None: """ @@ -201,7 +201,6 @@ async def get_object( self, object_type: Text, object_identifier: Text ) -> Optional[Dict[Text, Any]]: if object_type not in self.data: - return None objects = self.data[object_type] @@ -221,7 +220,6 @@ async def get_object( # if the object was referred to directly, we need to compare the representation # of each object with the given object identifier if not objects_of_interest: - repr_function = await utils.call_potential_coroutine( self.get_representation_function_of_object(object_type) ) diff --git a/rasa_sdk/knowledge_base/utils.py b/rasa_sdk/knowledge_base/utils.py index 85022cbe4..b8737212c 100644 --- a/rasa_sdk/knowledge_base/utils.py +++ b/rasa_sdk/knowledge_base/utils.py @@ -91,7 +91,7 @@ def resolve_mention( # NOTE: # for now we just assume that if the user refers to an object, for # example via "it" or "that restaurant", they are actually referring to the last - # object that was detected. + # object that was detected. # Since object type slot is reset to 'None' value, it is sufficient to only check # whether the last_object_type is not None. if last_object_type: diff --git a/scripts/release.py b/scripts/release.py index 01af33b53..6e7010cfe 100644 --- a/scripts/release.py +++ b/scripts/release.py @@ -161,7 +161,8 @@ def is_valid_version_number(v: Text) -> bool: str(current_version.next_release_candidate("major")), ] version = questionary.select( - f"Which {version} do you want to release?", choices=choices, + f"Which {version} do you want to release?", + choices=choices, ).ask() if version: @@ -255,8 +256,8 @@ def next_version(args: argparse.Namespace) -> Version: def generate_changelog(version: Version) -> None: """Call towncrier and create a changelog from all available changelog entries.""" check_call( - ["towncrier", "build", "--yes", "--version", str(version)], cwd=str(project_root()) - + ["towncrier", "build", "--yes", "--version", str(version)], + cwd=str(project_root()), ) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index 60985db30..b15c37f3d 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -30,7 +30,7 @@ def compare_slots(slot_list_1, slot_list_2): @pytest.mark.parametrize( "latest_message,slots,expected_slots", [ - ( + ( {}, { SLOT_MENTION: None, @@ -70,19 +70,16 @@ def compare_slots(slot_list_1, slot_list_2): SlotSet("cuisine", None), ], ), - ( + ( { - - "entities":[ + "entities": [ { - "entity":"attribute", - + "entity": "attribute", }, { - "entity":"mention", - } + "entity": "mention", + }, ], - }, { SLOT_MENTION: "2", @@ -101,18 +98,15 @@ def compare_slots(slot_list_1, slot_list_2): ], ), ( - { - - "entities":[ + { + "entities": [ { - "entity":"attribute", - + "entity": "attribute", }, { - "entity":"restaurant", - } + "entity": "restaurant", + }, ], - }, { SLOT_MENTION: None, @@ -133,9 +127,7 @@ def compare_slots(slot_list_1, slot_list_2): ), ( { - - "entities":[], - + "entities": [], }, { SLOT_MENTION: None, @@ -149,22 +141,23 @@ def compare_slots(slot_list_1, slot_list_2): ), ], ) -async def test_action_run(data_file, latest_message,slots, expected_slots): +async def test_action_run(data_file, latest_message, slots, expected_slots): knowledge_base = InMemoryKnowledgeBase(data_file) action = ActionQueryKnowledgeBase(knowledge_base) dispatcher = CollectingDispatcher() - - tracker = Tracker("default", slots, latest_message, [], False, None, {}, "action_listen") + + tracker = Tracker( + "default", slots, latest_message, [], False, None, {}, "action_listen" + ) actual_slots = await action.run(dispatcher, tracker, {}) - print(f"actual slots: {actual_slots}\nexpected slots:{expected_slots}") + print(f"actual slots: {actual_slots}\nexpected slots:{expected_slots}") compare_slots(expected_slots, actual_slots) compare_slots(actual_slots, expected_slots) # Check that utterances produced by action are correct. if slots[SLOT_ATTRIBUTE]: if slots.get("restaurant") is not None: - name = slots["restaurant"] attr = slots[SLOT_ATTRIBUTE] obj = await knowledge_base.get_object("restaurant", name) diff --git a/tests/knowledge_base/test_utils.py b/tests/knowledge_base/test_utils.py index 7e350fd75..49db20889 100644 --- a/tests/knowledge_base/test_utils.py +++ b/tests/knowledge_base/test_utils.py @@ -132,48 +132,43 @@ def test_get_object_name(slots, use_last_object_mention, expected_object_name): assert actual_object_name == expected_object_name - @pytest.mark.parametrize( "latest_message,object_types,expected_object_name", [ ( { - "entities":[ + "entities": [ { - "entity":"attribute", - "start":0, - "end":11, - "confidence_entity":0.9997496008872986, - "value":"price-range", - "extractor":"DIETClassifier", - "processors":[ - "EntitySynonymMapper" - ] + "entity": "attribute", + "start": 0, + "end": 11, + "confidence_entity": 0.9997496008872986, + "value": "price-range", + "extractor": "DIETClassifier", + "processors": ["EntitySynonymMapper"], }, { - "entity":"restaurant", - "start":15, - "end":21, - "confidence_entity":0.9953670501708984, - "value":"Donath", - "extractor":"DIETClassifier" - } + "entity": "restaurant", + "start": 15, + "end": 21, + "confidence_entity": 0.9953670501708984, + "value": "Donath", + "extractor": "DIETClassifier", + }, ], - }, - ['hotel', 'restaurant'], - 'restaurant' - + ["hotel", "restaurant"], + "restaurant", ), - - ] + ], ) -def test_match_extracted_entities_to_object_types(latest_message, object_types, expected_object_name): - - tracker = Tracker("default", {}, latest_message, [], False, None, {}, "action_listen") - actual_object_name = match_extracted_entities_to_object_types( - tracker,object_types +def test_match_extracted_entities_to_object_types( + latest_message, object_types, expected_object_name +): + tracker = Tracker( + "default", {}, latest_message, [], False, None, {}, "action_listen" ) - print(actual_object_name , expected_object_name) + actual_object_name = match_extracted_entities_to_object_types(tracker, object_types) + print(actual_object_name, expected_object_name) - assert actual_object_name == expected_object_name \ No newline at end of file + assert actual_object_name == expected_object_name From 8e0536acff3821c79275f852d01e3cc8054341c8 Mon Sep 17 00:00:00 2001 From: Dilanka96 Date: Wed, 1 Mar 2023 15:33:27 +0530 Subject: [PATCH 34/94] Added line --- rasa_sdk/executor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rasa_sdk/executor.py b/rasa_sdk/executor.py index 341d33515..70eeb8fa8 100644 --- a/rasa_sdk/executor.py +++ b/rasa_sdk/executor.py @@ -24,6 +24,7 @@ class CollectingDispatcher: """Send messages back to user""" def __init__(self) -> None: + self.messages: List[Dict[Text, Any]] = [] def utter_message( From 6be53a98723c46f4827826ac3f3301296dbb2729 Mon Sep 17 00:00:00 2001 From: Dilanka96 Date: Wed, 1 Mar 2023 15:35:20 +0530 Subject: [PATCH 35/94] Removed tab --- rasa_sdk/executor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_sdk/executor.py b/rasa_sdk/executor.py index 70eeb8fa8..b1377fe15 100644 --- a/rasa_sdk/executor.py +++ b/rasa_sdk/executor.py @@ -24,7 +24,7 @@ class CollectingDispatcher: """Send messages back to user""" def __init__(self) -> None: - + self.messages: List[Dict[Text, Any]] = [] def utter_message( From 6ea590744702a20fb883da148fe80ddd40f77245 Mon Sep 17 00:00:00 2001 From: Dilanka96 Date: Thu, 2 Mar 2023 12:20:52 +0530 Subject: [PATCH 36/94] Resolved all linting issues - flake8 --- rasa_sdk/knowledge_base/actions.py | 4 +-- rasa_sdk/knowledge_base/storage.py | 2 ++ rasa_sdk/knowledge_base/utils.py | 2 +- tests/knowledge_base/test_actions.py | 36 ++++++++++-------------- tests/knowledge_base/test_utils.py | 41 ++++++++++++---------------- 5 files changed, 38 insertions(+), 47 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 9ecbbbaec..70f2c1676 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -135,8 +135,8 @@ async def run( object_types = self.knowledge_base.get_object_types() object_type = match_extracted_entities_to_object_types(tracker, object_types) set_object_type_slot_event = [SlotSet(SLOT_OBJECT_TYPE, object_type)] - tracker.add_slots(set_object_type_slot_event) # temporarily set the `object_type_slot` to extracted value - + tracker.add_slots(set_object_type_slot_event) # temporarily set the `object_type_slot` to extracted value + if object_type and attribute: return await self._query_attribute( dispatcher, object_type, attribute, tracker diff --git a/rasa_sdk/knowledge_base/storage.py b/rasa_sdk/knowledge_base/storage.py index 4f6ab3466..87a22abee 100644 --- a/rasa_sdk/knowledge_base/storage.py +++ b/rasa_sdk/knowledge_base/storage.py @@ -110,7 +110,9 @@ async def get_object( """ raise NotImplementedError("Method is not implemented.") + class InMemoryKnowledgeBase(KnowledgeBase): + def __init__(self, data_file: Text) -> None: """ Initialize the in-memory knowledge base. diff --git a/rasa_sdk/knowledge_base/utils.py b/rasa_sdk/knowledge_base/utils.py index 85022cbe4..b8737212c 100644 --- a/rasa_sdk/knowledge_base/utils.py +++ b/rasa_sdk/knowledge_base/utils.py @@ -91,7 +91,7 @@ def resolve_mention( # NOTE: # for now we just assume that if the user refers to an object, for # example via "it" or "that restaurant", they are actually referring to the last - # object that was detected. + # object that was detected. # Since object type slot is reset to 'None' value, it is sufficient to only check # whether the last_object_type is not None. if last_object_type: diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index 60985db30..2c671f531 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -30,7 +30,7 @@ def compare_slots(slot_list_1, slot_list_2): @pytest.mark.parametrize( "latest_message,slots,expected_slots", [ - ( + ( {}, { SLOT_MENTION: None, @@ -70,20 +70,18 @@ def compare_slots(slot_list_1, slot_list_2): SlotSet("cuisine", None), ], ), - ( + ( { - - "entities":[ + "entities" : [ { - "entity":"attribute", - + "entity": "attribute", }, { - "entity":"mention", + "entity": " mention", } ], - }, + { SLOT_MENTION: "2", SLOT_ATTRIBUTE: "cuisine", @@ -101,19 +99,17 @@ def compare_slots(slot_list_1, slot_list_2): ], ), ( - { - - "entities":[ + { + "entities": [ { - "entity":"attribute", - + "entity": "attribute", }, { - "entity":"restaurant", + "entity": "restaurant", } ], - }, + { SLOT_MENTION: None, SLOT_ATTRIBUTE: "cuisine", @@ -133,9 +129,7 @@ def compare_slots(slot_list_1, slot_list_2): ), ( { - - "entities":[], - + "entities": [], }, { SLOT_MENTION: None, @@ -149,15 +143,15 @@ def compare_slots(slot_list_1, slot_list_2): ), ], ) -async def test_action_run(data_file, latest_message,slots, expected_slots): +async def test_action_run(data_file, latest_message, slots, expected_slots): knowledge_base = InMemoryKnowledgeBase(data_file) action = ActionQueryKnowledgeBase(knowledge_base) dispatcher = CollectingDispatcher() - + tracker = Tracker("default", slots, latest_message, [], False, None, {}, "action_listen") actual_slots = await action.run(dispatcher, tracker, {}) - print(f"actual slots: {actual_slots}\nexpected slots:{expected_slots}") + print(f"actual slots: {actual_slots}\nexpected slots:{expected_slots}") compare_slots(expected_slots, actual_slots) compare_slots(actual_slots, expected_slots) diff --git a/tests/knowledge_base/test_utils.py b/tests/knowledge_base/test_utils.py index 7e350fd75..9c7cf4c87 100644 --- a/tests/knowledge_base/test_utils.py +++ b/tests/knowledge_base/test_utils.py @@ -132,48 +132,43 @@ def test_get_object_name(slots, use_last_object_mention, expected_object_name): assert actual_object_name == expected_object_name - @pytest.mark.parametrize( "latest_message,object_types,expected_object_name", [ ( { - "entities":[ + "entities": [ { - "entity":"attribute", - "start":0, - "end":11, - "confidence_entity":0.9997496008872986, - "value":"price-range", - "extractor":"DIETClassifier", - "processors":[ - "EntitySynonymMapper" + "entity": "attribute", + "start": 0, + "end": 11, + "confidence_entity": 0.9997496008872986, + "value": "price-range", + "extractor": "DIETClassifier", + "processors": [ + "EntitySynonymMapper" ] }, { - "entity":"restaurant", - "start":15, - "end":21, - "confidence_entity":0.9953670501708984, - "value":"Donath", - "extractor":"DIETClassifier" + "entity": "restaurant", + "start": 15, + "end": 21, + "confidence_entity": 0.9953670501708984, + "value": "Donath", + "extractor": "DIETClassifier" } ], - }, ['hotel', 'restaurant'], 'restaurant' - ), ] ) def test_match_extracted_entities_to_object_types(latest_message, object_types, expected_object_name): - + tracker = Tracker("default", {}, latest_message, [], False, None, {}, "action_listen") actual_object_name = match_extracted_entities_to_object_types( - tracker,object_types + tracker, object_types ) - print(actual_object_name , expected_object_name) - - assert actual_object_name == expected_object_name \ No newline at end of file + assert actual_object_name == expected_object_name From 9bd91a3e1bb4d35fe35c33a68ebae6ccce51f03d Mon Sep 17 00:00:00 2001 From: Dilanka96 Date: Thu, 2 Mar 2023 16:32:18 +0530 Subject: [PATCH 37/94] Fixed Errors --- tests/knowledge_base/test_actions.py | 8 ++------ tests/knowledge_base/test_utils.py | 2 -- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index 511b05755..354bc06ec 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -30,7 +30,6 @@ def compare_slots(slot_list_1, slot_list_2): @pytest.mark.parametrize( "latest_message,slots,expected_slots", [ - ( ( {}, { @@ -71,7 +70,7 @@ def compare_slots(slot_list_1, slot_list_2): SlotSet("cuisine", None), ], ), - ( + ( { "entities" : [ @@ -103,8 +102,7 @@ def compare_slots(slot_list_1, slot_list_2): ), ( { - "entities": [ - { + "entities": [ { "entity": "attribute", @@ -136,7 +134,6 @@ def compare_slots(slot_list_1, slot_list_2): ( { "entities": [], - "entities": [], }, { SLOT_MENTION: None, @@ -150,7 +147,6 @@ def compare_slots(slot_list_1, slot_list_2): ), ], ) -async def test_action_run(data_file, latest_message, slots, expected_slots): async def test_action_run(data_file, latest_message, slots, expected_slots): knowledge_base = InMemoryKnowledgeBase(data_file) action = ActionQueryKnowledgeBase(knowledge_base) diff --git a/tests/knowledge_base/test_utils.py b/tests/knowledge_base/test_utils.py index cc56dc3d8..9ef639c0f 100644 --- a/tests/knowledge_base/test_utils.py +++ b/tests/knowledge_base/test_utils.py @@ -137,7 +137,6 @@ def test_get_object_name(slots, use_last_object_mention, expected_object_name): [ ( { - "entities": [ "entities": [ { "entity": "attribute", @@ -172,4 +171,3 @@ def test_match_extracted_entities_to_object_types(latest_message, object_types, tracker, object_types ) assert actual_object_name == expected_object_name - From 0436f776929373fc26e9828a9f41143c0c882317 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 10 Mar 2023 17:13:53 +0530 Subject: [PATCH 38/94] formatted with black --- rasa_sdk/knowledge_base/actions.py | 8 ++++++-- rasa_sdk/knowledge_base/storage.py | 1 - 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 70f2c1676..6684a969d 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -133,9 +133,13 @@ async def run( # sets the object type dynamically from entities if object_type is not found # in user query object_types = self.knowledge_base.get_object_types() - object_type = match_extracted_entities_to_object_types(tracker, object_types) + object_type = match_extracted_entities_to_object_types( + tracker, object_types + ) set_object_type_slot_event = [SlotSet(SLOT_OBJECT_TYPE, object_type)] - tracker.add_slots(set_object_type_slot_event) # temporarily set the `object_type_slot` to extracted value + tracker.add_slots( + set_object_type_slot_event + ) # temporarily set the `object_type_slot` to extracted value if object_type and attribute: return await self._query_attribute( diff --git a/rasa_sdk/knowledge_base/storage.py b/rasa_sdk/knowledge_base/storage.py index 93c908321..16e222719 100644 --- a/rasa_sdk/knowledge_base/storage.py +++ b/rasa_sdk/knowledge_base/storage.py @@ -111,7 +111,6 @@ async def get_object( class InMemoryKnowledgeBase(KnowledgeBase): - def __init__(self, data_file: Text) -> None: """ Initialize the in-memory knowledge base. From d30fe8cfb7b9170ebe1f1651ee46fa62a7b4878e Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 31 Mar 2023 16:39:00 +0530 Subject: [PATCH 39/94] removed entity:attribute appearing twice --- tests/knowledge_base/test_actions.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index 354bc06ec..6ae533c74 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -76,7 +76,6 @@ def compare_slots(slot_list_1, slot_list_2): "entities" : [ { "entity": "attribute", - "entity": "attribute", }, { "entity": " mention", @@ -106,7 +105,6 @@ def compare_slots(slot_list_1, slot_list_2): "entities": [ { "entity": "attribute", - "entity": "attribute", }, { "entity": "restaurant", From 10ef8c579d8ff9cae660376164702654b130ff8c Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 31 Mar 2023 17:26:55 +0530 Subject: [PATCH 40/94] removed white space to meet max line charac requirement --- tests/knowledge_base/test_actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index 6ae533c74..20e8e7b02 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -151,7 +151,7 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): dispatcher = CollectingDispatcher() - tracker = Tracker("default", slots, latest_message, [], False, None, {}, "action_listen") + tracker = Tracker("default",slots,latest_message,[],False,None,{},"action_listen") actual_slots = await action.run(dispatcher, tracker, {}) print(f"actual slots: {actual_slots}\nexpected slots:{expected_slots}") compare_slots(expected_slots, actual_slots) From 3091b1a55e006d59a78e5cb2c074377b220069e7 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 31 Mar 2023 17:29:33 +0530 Subject: [PATCH 41/94] entered variables into new line to meet max line charac requirements --- tests/knowledge_base/test_utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/knowledge_base/test_utils.py b/tests/knowledge_base/test_utils.py index 9ef639c0f..12eec0e66 100644 --- a/tests/knowledge_base/test_utils.py +++ b/tests/knowledge_base/test_utils.py @@ -164,7 +164,10 @@ def test_get_object_name(slots, use_last_object_mention, expected_object_name): ), ], ) -def test_match_extracted_entities_to_object_types(latest_message, object_types, expected_object_name): +def test_match_extracted_entities_to_object_types(latest_message, + object_types, + expected_object_name + ): tracker = Tracker("default", {}, latest_message, [], False, None, {}, "action_listen") actual_object_name = match_extracted_entities_to_object_types( From 4bb6842b53ced8a2464ba0ccffbd518b333edc02 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 31 Mar 2023 17:32:15 +0530 Subject: [PATCH 42/94] removed print statement --- tests/knowledge_base/test_actions.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index 20e8e7b02..d744df6b0 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -151,9 +151,8 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): dispatcher = CollectingDispatcher() - tracker = Tracker("default",slots,latest_message,[],False,None,{},"action_listen") + tracker = Tracker("default", slots, latest_message, [], False, None, {}, "action_listen") actual_slots = await action.run(dispatcher, tracker, {}) - print(f"actual slots: {actual_slots}\nexpected slots:{expected_slots}") compare_slots(expected_slots, actual_slots) compare_slots(actual_slots, expected_slots) From 27b5ba681fc04a8f910c75dc1e814b07f15b521a Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 31 Mar 2023 18:34:04 +0530 Subject: [PATCH 43/94] reverting the line indentation --- tests/knowledge_base/test_utils.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/knowledge_base/test_utils.py b/tests/knowledge_base/test_utils.py index 12eec0e66..9ef639c0f 100644 --- a/tests/knowledge_base/test_utils.py +++ b/tests/knowledge_base/test_utils.py @@ -164,10 +164,7 @@ def test_get_object_name(slots, use_last_object_mention, expected_object_name): ), ], ) -def test_match_extracted_entities_to_object_types(latest_message, - object_types, - expected_object_name - ): +def test_match_extracted_entities_to_object_types(latest_message, object_types, expected_object_name): tracker = Tracker("default", {}, latest_message, [], False, None, {}, "action_listen") actual_object_name = match_extracted_entities_to_object_types( From 3d321da0a8e96ed7bf5c70c46074b6ab910bf88f Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 31 Mar 2023 18:39:27 +0530 Subject: [PATCH 44/94] formatted code with black --- tests/knowledge_base/test_actions.py | 14 ++++++-------- tests/knowledge_base/test_utils.py | 23 +++++++++++------------ 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index d744df6b0..b0381e997 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -70,19 +70,17 @@ def compare_slots(slot_list_1, slot_list_2): SlotSet("cuisine", None), ], ), - ( { - "entities" : [ + "entities": [ { "entity": "attribute", }, { "entity": " mention", - } + }, ], }, - { SLOT_MENTION: "2", SLOT_ATTRIBUTE: "cuisine", @@ -101,17 +99,15 @@ def compare_slots(slot_list_1, slot_list_2): ), ( { - "entities": [ { "entity": "attribute", }, { "entity": "restaurant", - } + }, ], }, - { SLOT_MENTION: None, SLOT_ATTRIBUTE: "cuisine", @@ -151,7 +147,9 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): dispatcher = CollectingDispatcher() - tracker = Tracker("default", slots, latest_message, [], False, None, {}, "action_listen") + tracker = Tracker( + "default", slots, latest_message, [], False, None, {}, "action_listen" + ) actual_slots = await action.run(dispatcher, tracker, {}) compare_slots(expected_slots, actual_slots) compare_slots(actual_slots, expected_slots) diff --git a/tests/knowledge_base/test_utils.py b/tests/knowledge_base/test_utils.py index 6d0c927e8..0e9c82058 100644 --- a/tests/knowledge_base/test_utils.py +++ b/tests/knowledge_base/test_utils.py @@ -145,9 +145,7 @@ def test_get_object_name(slots, use_last_object_mention, expected_object_name): "confidence_entity": 0.9997496008872986, "value": "price-range", "extractor": "DIETClassifier", - "processors": [ - "EntitySynonymMapper" - ] + "processors": ["EntitySynonymMapper"], }, { "entity": "restaurant", @@ -155,19 +153,20 @@ def test_get_object_name(slots, use_last_object_mention, expected_object_name): "end": 21, "confidence_entity": 0.9953670501708984, "value": "Donath", - "extractor": "DIETClassifier" - } + "extractor": "DIETClassifier", + }, ], }, - ['hotel', 'restaurant'], - 'restaurant' + ["hotel", "restaurant"], + "restaurant", ), ], ) -def test_match_extracted_entities_to_object_types(latest_message, object_types, expected_object_name): - - tracker = Tracker("default", {}, latest_message, [], False, None, {}, "action_listen") - actual_object_name = match_extracted_entities_to_object_types( - tracker, object_types +def test_match_extracted_entities_to_object_types( + latest_message, object_types, expected_object_name +): + tracker = Tracker( + "default", {}, latest_message, [], False, None, {}, "action_listen" ) + actual_object_name = match_extracted_entities_to_object_types(tracker, object_types) assert actual_object_name == expected_object_name From f3cc64e73df1f897953c8e176c0fb0540424402b Mon Sep 17 00:00:00 2001 From: Gajithra Date: Sun, 2 Apr 2023 09:33:35 +0530 Subject: [PATCH 45/94] improved language and fixed typos --- changelog/922.improvement.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/changelog/922.improvement.md b/changelog/922.improvement.md index b0ee68034..c50da5dd2 100644 --- a/changelog/922.improvement.md +++ b/changelog/922.improvement.md @@ -1,5 +1,5 @@ ## Problem -Rasa knowledge base actions cannot infer the object type of an object directly from the user message. This prevents action_query_knowledge from providing a suitable response when a user asks for a certain attribute of an object unless the user first asks to list objects related to that object type even though the knowledge base has the relevant information. That is, the knowledge base actions require the slot `object_type` to be set to one of the primary key values in the knowledge base for it to search through the objects. Here is an example: +Rasa knowledge base actions cannot infer the object type of an object directly from the user message without the user first asking to list all objects related to that object type. This prevents action_query_knowledge from providing a suitable response when a user asks for a certain attribute of an object even though the knowledge base has the relevant information. That is, the knowledge base actions require the slot `object_type` to be set to one of the primary key values in the knowledge base for it to search through the objects. Here is an example: ``` Your input -> what is the price range of Berlin Burrito Company? Sorry, I'm not sure I understand. Can you rephrase? @@ -16,16 +16,14 @@ Your input -> what is the price range of Berlin Burrito Company? ## Proposed solution - The improvement requires changes to the classes ActionQueryKnowledgeBase and InMemoryKnowledgeBase under rasa-sdk. -- The `object_type` can be inferred by utilizing the entity classification (DIET) where object types are used as entities to annotate object names. +- The `object_type` can be inferred by utilizing the entity extraction (DIET) where object types are used as entities to annotate object names. This also requires changes to be made to slot management to enable dynamic inference of `object_type`. - The scope of suggested solution is limited to user queries where they ask for an attribute of a given object without mentioning the object type and without having the need to first ask a list of options of corresponding object type. - E.g: If the user asks ‘price range of Berlin Burrito Company’, then rasa will extract and set attribute slot value to ‘price-range’ and hotel slot value to ‘Berlin Burrito Company’. From this it can be inferred that the user is talking about object type ‘hotel’. ## Summary of Changes - In order to enable the inferance of `object_type` using the entities the following changes were made to existing code base: - - Extract the list of object_types from our knowledge base using a new function `get_object_types()` in `storage.py` under `InMemoryKnowledgeBase` calss. - - A new function named `match_extracted_entities_to_object_types` was added in `utils.py` to infer the object type of a given object using the entities and list of object types + - Extract the list of object_types from our knowledge base using a new method `get_object_types()` in `storage.py` for `InMemoryKnowledgeBase` class. + - A new method named `match_extracted_entities_to_object_types()` was added in `utils.py` to infer the object type of a given object using the entities and list of object types - The relevant logic was added in `actions.py` to infer the object type using the above functionalities when object type slot is not set. - - To enable dynamic inference of `object_type`, changes to slot management is also required. Currently, the change made is to return None value for `object_type` slot. - - + - To enable dynamic inference of `object_type`, changes to slot management is also required. Currently, the change made is to reset `object_type` slot to `None` after every conversation turn. \ No newline at end of file From bace082c115de2251daecc57eca0d684bc68db87 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Sun, 2 Apr 2023 09:40:30 +0530 Subject: [PATCH 46/94] added get_object_types method in KnowledgeBase parent class --- rasa_sdk/knowledge_base/storage.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rasa_sdk/knowledge_base/storage.py b/rasa_sdk/knowledge_base/storage.py index d7911590e..335f59dd2 100644 --- a/rasa_sdk/knowledge_base/storage.py +++ b/rasa_sdk/knowledge_base/storage.py @@ -109,6 +109,15 @@ async def get_object( """ raise NotImplementedError("Method is not implemented.") + def get_object_types(self) -> List[Text]: + """ + Returns a list of object types from In Memory Data. + + Returns: the list of object types + + """ + raise NotImplementedError("Method is not implemented.") + class InMemoryKnowledgeBase(KnowledgeBase): def __init__(self, data_file: Text) -> None: From 4771ff4b7953f5713a018600397de0eb42da2836 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Sun, 2 Apr 2023 09:45:51 +0530 Subject: [PATCH 47/94] renamned entities_values to entity_names in line 188 --- rasa_sdk/knowledge_base/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rasa_sdk/knowledge_base/utils.py b/rasa_sdk/knowledge_base/utils.py index 3feb36bbf..7290082cf 100644 --- a/rasa_sdk/knowledge_base/utils.py +++ b/rasa_sdk/knowledge_base/utils.py @@ -185,8 +185,8 @@ def match_extracted_entities_to_object_types( Returns: the name of the object type """ entities = tracker.latest_message.get("entities", []) - entities_values = [entity.get("entity") for entity in entities] - for entity in entities_values: + entity_names = [entity.get("entity") for entity in entities] + for entity in entity_names: if entity in object_types: return entity From 618b8831c79b250ccce8b51dd4780bfd4f02c25b Mon Sep 17 00:00:00 2001 From: Gajithra Date: Sun, 2 Apr 2023 09:46:57 +0530 Subject: [PATCH 48/94] removed duplicated line in line 148 --- rasa_sdk/knowledge_base/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rasa_sdk/knowledge_base/utils.py b/rasa_sdk/knowledge_base/utils.py index 7290082cf..3300627e9 100644 --- a/rasa_sdk/knowledge_base/utils.py +++ b/rasa_sdk/knowledge_base/utils.py @@ -145,7 +145,6 @@ def reset_attribute_slots( what attributes are detected by the NER. We take all attributes that are set, e.g. cuisine = Italian. If we don't reset the attribute slots after the request is done and the next utterance of the user would be, for example, "List all - is done and the next utterance of the user would be, for example, "List all restaurants that have wifi.", we would have two attribute slots set: "wifi" and "cuisine". Thus, we would filter all restaurants for two attributes now: wifi = True and cuisine = Italian. However, the user did not specify any From 892f1616df6c96b71f1c7fe8ef6ee9e405483ee0 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Sun, 2 Apr 2023 09:47:58 +0530 Subject: [PATCH 49/94] modified return statement in function docstring --- rasa_sdk/knowledge_base/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_sdk/knowledge_base/utils.py b/rasa_sdk/knowledge_base/utils.py index 3300627e9..32b24320e 100644 --- a/rasa_sdk/knowledge_base/utils.py +++ b/rasa_sdk/knowledge_base/utils.py @@ -181,7 +181,7 @@ def match_extracted_entities_to_object_types( tracker: the tracker object_types: list of object types in the knowledge base - Returns: the name of the object type + Returns: the name of the object type if found, otherwise `None`. """ entities = tracker.latest_message.get("entities", []) entity_names = [entity.get("entity") for entity in entities] From 2e610e33fb98b892c33dc7dfd803fa6eb7322c58 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Sun, 2 Apr 2023 09:52:32 +0530 Subject: [PATCH 50/94] updated the match_extracted_entities_to_object_types doc string to mention the assumption that the user message only contains reference to one object type --- rasa_sdk/knowledge_base/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rasa_sdk/knowledge_base/utils.py b/rasa_sdk/knowledge_base/utils.py index 32b24320e..7ae7bed0f 100644 --- a/rasa_sdk/knowledge_base/utils.py +++ b/rasa_sdk/knowledge_base/utils.py @@ -175,7 +175,8 @@ def match_extracted_entities_to_object_types( If the user ask a question about an attribute using an object name and without specifying the object type, then this function searches the corresponding object type. (e.g: when user asks'price range of B&B', this - function detects the object type as 'hotel') + function extracts the object type as 'hotel'). Here we assume that the user message contains reference only to one + object type in the knowledge base. Args: tracker: the tracker From 4ae858bef80bbc8b070121fd5631865329cdd1bd Mon Sep 17 00:00:00 2001 From: Gajithra Date: Sun, 2 Apr 2023 10:00:28 +0530 Subject: [PATCH 51/94] renamed match_extracted_entities_to_object_types to match_extracted_entities_to_object_type --- changelog/922.improvement.md | 2 +- rasa_sdk/knowledge_base/actions.py | 4 ++-- rasa_sdk/knowledge_base/utils.py | 2 +- tests/knowledge_base/test_utils.py | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/changelog/922.improvement.md b/changelog/922.improvement.md index b0ee68034..768fc21b6 100644 --- a/changelog/922.improvement.md +++ b/changelog/922.improvement.md @@ -24,7 +24,7 @@ This also requires changes to be made to slot management to enable dynamic infer ## Summary of Changes - In order to enable the inferance of `object_type` using the entities the following changes were made to existing code base: - Extract the list of object_types from our knowledge base using a new function `get_object_types()` in `storage.py` under `InMemoryKnowledgeBase` calss. - - A new function named `match_extracted_entities_to_object_types` was added in `utils.py` to infer the object type of a given object using the entities and list of object types + - A new function named `match_extracted_entities_to_object_type` was added in `utils.py` to infer the object type of a given object using the entities and list of object types - The relevant logic was added in `actions.py` to infer the object type using the above functionalities when object type slot is not set. - To enable dynamic inference of `object_type`, changes to slot management is also required. Currently, the change made is to return None value for `object_type` slot. diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 229a299bc..6582cc8a8 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -13,7 +13,7 @@ SLOT_LISTED_OBJECTS, get_object_name, get_attribute_slots, - match_extracted_entities_to_object_types, + match_extracted_entities_to_object_type, ) from rasa_sdk import utils from rasa_sdk.executor import CollectingDispatcher @@ -139,7 +139,7 @@ async def run( # sets the object type dynamically from entities if object_type is not found # in user query object_types = self.knowledge_base.get_object_types() - object_type = match_extracted_entities_to_object_types( + object_type = match_extracted_entities_to_object_type( tracker, object_types ) set_object_type_slot_event = [SlotSet(SLOT_OBJECT_TYPE, object_type)] diff --git a/rasa_sdk/knowledge_base/utils.py b/rasa_sdk/knowledge_base/utils.py index 3feb36bbf..f7974d3f8 100644 --- a/rasa_sdk/knowledge_base/utils.py +++ b/rasa_sdk/knowledge_base/utils.py @@ -168,7 +168,7 @@ def reset_attribute_slots( return slots -def match_extracted_entities_to_object_types( +def match_extracted_entities_to_object_type( tracker: "Tracker", object_types: List[Text], ) -> Optional[Text]: diff --git a/tests/knowledge_base/test_utils.py b/tests/knowledge_base/test_utils.py index 0e9c82058..fe652e261 100644 --- a/tests/knowledge_base/test_utils.py +++ b/tests/knowledge_base/test_utils.py @@ -3,7 +3,7 @@ from rasa_sdk import Tracker from rasa_sdk.events import SlotSet from rasa_sdk.knowledge_base.utils import ( - match_extracted_entities_to_object_types, + match_extracted_entities_to_object_type, get_attribute_slots, reset_attribute_slots, get_object_name, @@ -162,11 +162,11 @@ def test_get_object_name(slots, use_last_object_mention, expected_object_name): ), ], ) -def test_match_extracted_entities_to_object_types( +def test_match_extracted_entities_to_object_type( latest_message, object_types, expected_object_name ): tracker = Tracker( "default", {}, latest_message, [], False, None, {}, "action_listen" ) - actual_object_name = match_extracted_entities_to_object_types(tracker, object_types) + actual_object_name = match_extracted_entities_to_object_type(tracker, object_types) assert actual_object_name == expected_object_name From c062dfff1f1186331a10b24715fdba18eefbaf74 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Sun, 2 Apr 2023 10:01:08 +0530 Subject: [PATCH 52/94] renamed match_extracted_entities_to_object_types to match_extracted_entities_to_object_type --- changelog/922.improvement.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/922.improvement.md b/changelog/922.improvement.md index c50da5dd2..2fb7be7aa 100644 --- a/changelog/922.improvement.md +++ b/changelog/922.improvement.md @@ -24,6 +24,6 @@ This also requires changes to be made to slot management to enable dynamic infer ## Summary of Changes - In order to enable the inferance of `object_type` using the entities the following changes were made to existing code base: - Extract the list of object_types from our knowledge base using a new method `get_object_types()` in `storage.py` for `InMemoryKnowledgeBase` class. - - A new method named `match_extracted_entities_to_object_types()` was added in `utils.py` to infer the object type of a given object using the entities and list of object types + - A new method named `match_extracted_entities_to_object_type()` was added in `utils.py` to infer the object type of a given object using the entities and list of object types - The relevant logic was added in `actions.py` to infer the object type using the above functionalities when object type slot is not set. - To enable dynamic inference of `object_type`, changes to slot management is also required. Currently, the change made is to reset `object_type` slot to `None` after every conversation turn. \ No newline at end of file From bfe9d87d42131f8dfade0ab56f4f8063f58c271e Mon Sep 17 00:00:00 2001 From: Gajithra Date: Sun, 2 Apr 2023 10:19:33 +0530 Subject: [PATCH 53/94] reverting a change that wasn't made --- rasa_sdk/knowledge_base/actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 229a299bc..8018014bb 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -234,7 +234,7 @@ async def _query_attribute( self.use_last_object_mention, ) - if not object_name or not attribute: + if object_name is None or not attribute: dispatcher.utter_message(response="utter_ask_rephrase") return [SlotSet(SLOT_MENTION, None)] From 3e9a3144d70398acd3b3a16ef27f107ff4c561c3 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Sun, 2 Apr 2023 10:24:06 +0530 Subject: [PATCH 54/94] improved doc string --- rasa_sdk/knowledge_base/storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_sdk/knowledge_base/storage.py b/rasa_sdk/knowledge_base/storage.py index 335f59dd2..7c3e3f718 100644 --- a/rasa_sdk/knowledge_base/storage.py +++ b/rasa_sdk/knowledge_base/storage.py @@ -111,7 +111,7 @@ async def get_object( def get_object_types(self) -> List[Text]: """ - Returns a list of object types from In Memory Data. + Returns a list of object types from knowledge base data. Returns: the list of object types From 5b4d380a73603f652f020ae1f70effb232b5b40e Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 11 Apr 2023 15:59:00 +0530 Subject: [PATCH 55/94] changed to --- rasa_sdk/knowledge_base/actions.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 229a299bc..60b781831 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -198,8 +198,10 @@ async def _query_objects( ) last_object = None if len(objects) > 1 else objects[0][key_attribute] + # reset the SLOT_OBJECT_TYPE to None. This ensures that the + # list of objects slots = [ - SlotSet(SLOT_OBJECT_TYPE, None), + SlotSet(SLOT_OBJECT_TYPE, None), SlotSet(SLOT_MENTION, None), SlotSet(SLOT_ATTRIBUTE, None), SlotSet(SLOT_LAST_OBJECT, last_object), @@ -234,7 +236,7 @@ async def _query_attribute( self.use_last_object_mention, ) - if not object_name or not attribute: + if object_name is None or not attribute: dispatcher.utter_message(response="utter_ask_rephrase") return [SlotSet(SLOT_MENTION, None)] From f7e9703edb5865ba2bf37dc4964d36512f9fd115 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Tue, 11 Apr 2023 16:02:41 +0530 Subject: [PATCH 56/94] added comment on slot object type resetting --- rasa_sdk/knowledge_base/actions.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 60b781831..3821859a5 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -198,8 +198,9 @@ async def _query_objects( ) last_object = None if len(objects) > 1 else objects[0][key_attribute] - # reset the SLOT_OBJECT_TYPE to None. This ensures that the - # list of objects + + # reset the SLOT_OBJECT_TYPE to None. This enables the user to ask about an object attribute + # without first asking to list the objects for an object type in order to set this slot. slots = [ SlotSet(SLOT_OBJECT_TYPE, None), SlotSet(SLOT_MENTION, None), @@ -267,7 +268,8 @@ async def _query_attribute( dispatcher, object_representation, attribute, value ) ) - + # reset the SLOT_OBJECT_TYPE to None. This enables the user to ask about an object attribute + # without first asking to list the objects for an object type in order to set this slot. slots = [ SlotSet(SLOT_OBJECT_TYPE, None), SlotSet(SLOT_ATTRIBUTE, None), From 61a08440a4c909dbb7853195486eea64043bd3bb Mon Sep 17 00:00:00 2001 From: Gajithra Date: Wed, 19 Apr 2023 13:38:28 +0530 Subject: [PATCH 57/94] modified if condition in run to check whether latest message attribute --- rasa_sdk/knowledge_base/actions.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index f8e63bfba..eb5a571d3 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -132,8 +132,10 @@ async def run( last_object_type = tracker.get_slot(SLOT_LAST_OBJECT_TYPE) attribute = tracker.get_slot(SLOT_ATTRIBUTE) has_mention = tracker.get_slot(SLOT_MENTION) is not None - - new_request = object_type != last_object_type + entities_in_latest_message = [entity.get("entity") for entity in tracker.latest_message["entities"]] + has_attribute_in_latest = True if 'attribute' in entities_in_latest_message else False + + # new_request = object_type != last_object_type if not object_type: # sets the object type dynamically from entities if object_type is not found @@ -147,12 +149,12 @@ async def run( set_object_type_slot_event ) # temporarily set the `object_type_slot` to extracted value - if object_type and attribute: + if (object_type and not has_attribute_in_latest): + return await self._query_objects(dispatcher, object_type, tracker) + elif object_type and attribute: return await self._query_attribute( dispatcher, object_type, attribute, tracker ) - elif (object_type and not attribute) or (object_type and new_request): - return await self._query_objects(dispatcher, object_type, tracker) if last_object_type and has_mention and attribute: return await self._query_attribute( From 0c952d793ad7e4c00604f7fb31d8e81585e5e804 Mon Sep 17 00:00:00 2001 From: Dilanka96 Date: Wed, 26 Apr 2023 10:03:00 +0530 Subject: [PATCH 58/94] Resolved test_actions failures --- tests/knowledge_base/test_actions.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index b0381e997..a951aaedc 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -31,7 +31,13 @@ def compare_slots(slot_list_1, slot_list_2): "latest_message,slots,expected_slots", [ ( - {}, + { + "entities": [ + { + "entity": "object_type", + } + ], + }, { SLOT_MENTION: None, SLOT_ATTRIBUTE: None, @@ -50,7 +56,16 @@ def compare_slots(slot_list_1, slot_list_2): ], ), ( - {}, + { + "entities": [ + { + "entity": "object_type", + }, + { + "entity": "cuisine", + } + ], + }, { SLOT_MENTION: None, SLOT_ATTRIBUTE: None, From 795f9c825fcc1fcb44798225c4a4a7ac9ebd2e97 Mon Sep 17 00:00:00 2001 From: Dilanka96 Date: Thu, 27 Apr 2023 12:31:59 +0530 Subject: [PATCH 59/94] Added unit test assertion --- rasa_sdk/knowledge_base/actions.py | 6 ++---- tests/knowledge_base/test_actions.py | 29 ++++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index eb5a571d3..411fe15ff 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -134,12 +134,10 @@ async def run( has_mention = tracker.get_slot(SLOT_MENTION) is not None entities_in_latest_message = [entity.get("entity") for entity in tracker.latest_message["entities"]] has_attribute_in_latest = True if 'attribute' in entities_in_latest_message else False - - # new_request = object_type != last_object_type if not object_type: - # sets the object type dynamically from entities if object_type is not found - # in user query + # sets the object type dynamically from entities if object_type is not + # found in user query object_types = self.knowledge_base.get_object_types() object_type = match_extracted_entities_to_object_type( tracker, object_types diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index a951aaedc..e2b69b275 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -13,6 +13,7 @@ SLOT_LAST_OBJECT, SLOT_LAST_OBJECT_TYPE, ) +from rasa_sdk.knowledge_base.utils import match_extracted_entities_to_object_type def compare_slots(slot_list_1, slot_list_2): @@ -35,7 +36,7 @@ def compare_slots(slot_list_1, slot_list_2): "entities": [ { "entity": "object_type", - } + }, ], }, { @@ -63,7 +64,7 @@ def compare_slots(slot_list_1, slot_list_2): }, { "entity": "cuisine", - } + }, ], }, { @@ -112,6 +113,7 @@ def compare_slots(slot_list_1, slot_list_2): SlotSet(SLOT_LAST_OBJECT_TYPE, "restaurant"), ], ), + ( { "entities": [ @@ -140,6 +142,7 @@ def compare_slots(slot_list_1, slot_list_2): SlotSet(SLOT_LAST_OBJECT_TYPE, "restaurant"), ], ), + ( { "entities": [], @@ -154,6 +157,7 @@ def compare_slots(slot_list_1, slot_list_2): }, [], ), + ], ) async def test_action_run(data_file, latest_message, slots, expected_slots): @@ -192,3 +196,24 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): actual_msg = dispatcher.messages[0]["text"] assert actual_msg == expected_msg + + # Check that temporary slot setting by action is correct. + if not any(slots): + object_types = knowledge_base.get_object_types() + object_type = match_extracted_entities_to_object_type( + tracker, object_types + ) + set_object_type_slot_event = [SlotSet(SLOT_OBJECT_TYPE, object_type)] + tracker.add_slots( + set_object_type_slot_event + ) + if slots.get(SLOT_MENTION) is None: + expected_temp_object_type_value = 'restaurant' + actual_temp_object_type_value = tracker.slots[SLOT_OBJECT_TYPE] + + assert actual_temp_object_type_value == expected_temp_object_type_value + else: + expected_temp_object_type_value = None + actual_temp_object_type_value = tracker.slots[SLOT_OBJECT_TYPE] + + assert actual_temp_object_type_value == expected_temp_object_type_value From f575f56d763455781fb80e14c507cdcaa6bda35f Mon Sep 17 00:00:00 2001 From: Dilanka96 Date: Thu, 27 Apr 2023 14:00:58 +0530 Subject: [PATCH 60/94] Modified test_actions.py and actions.py --- rasa_sdk/knowledge_base/actions.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 411fe15ff..acd8874ff 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -132,8 +132,12 @@ async def run( last_object_type = tracker.get_slot(SLOT_LAST_OBJECT_TYPE) attribute = tracker.get_slot(SLOT_ATTRIBUTE) has_mention = tracker.get_slot(SLOT_MENTION) is not None - entities_in_latest_message = [entity.get("entity") for entity in tracker.latest_message["entities"]] - has_attribute_in_latest = True if 'attribute' in entities_in_latest_message else False + + has_attribute_in_latest = False + for entity in tracker.latest_message["entities"]: + if entity.get("entity") == 'attribute': + has_attribute_in_latest = True + break if not object_type: # sets the object type dynamically from entities if object_type is not From 0835ee26524cc5f40d0033d277ce3a182af4635b Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 28 Apr 2023 13:12:01 +0530 Subject: [PATCH 61/94] modified code logic to include in one line --- rasa_sdk/knowledge_base/actions.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index acd8874ff..859d0834b 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -132,12 +132,8 @@ async def run( last_object_type = tracker.get_slot(SLOT_LAST_OBJECT_TYPE) attribute = tracker.get_slot(SLOT_ATTRIBUTE) has_mention = tracker.get_slot(SLOT_MENTION) is not None - - has_attribute_in_latest = False - for entity in tracker.latest_message["entities"]: - if entity.get("entity") == 'attribute': - has_attribute_in_latest = True - break + + has_attribute_in_latest = any(entity.get("entity") == 'attribute' for entity in tracker.latest_message["entities"]) if not object_type: # sets the object type dynamically from entities if object_type is not From 5ce16582cd0cdc99958ff6e937cd8e29dc6b8a75 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 28 Apr 2023 13:13:51 +0530 Subject: [PATCH 62/94] formatted code with black --- rasa_sdk/knowledge_base/actions.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 859d0834b..9c5dba8ae 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -132,22 +132,23 @@ async def run( last_object_type = tracker.get_slot(SLOT_LAST_OBJECT_TYPE) attribute = tracker.get_slot(SLOT_ATTRIBUTE) has_mention = tracker.get_slot(SLOT_MENTION) is not None - - has_attribute_in_latest = any(entity.get("entity") == 'attribute' for entity in tracker.latest_message["entities"]) + + has_attribute_in_latest = any( + entity.get("entity") == "attribute" + for entity in tracker.latest_message["entities"] + ) if not object_type: # sets the object type dynamically from entities if object_type is not # found in user query object_types = self.knowledge_base.get_object_types() - object_type = match_extracted_entities_to_object_type( - tracker, object_types - ) + object_type = match_extracted_entities_to_object_type(tracker, object_types) set_object_type_slot_event = [SlotSet(SLOT_OBJECT_TYPE, object_type)] tracker.add_slots( set_object_type_slot_event ) # temporarily set the `object_type_slot` to extracted value - if (object_type and not has_attribute_in_latest): + if object_type and not has_attribute_in_latest: return await self._query_objects(dispatcher, object_type, tracker) elif object_type and attribute: return await self._query_attribute( From 0ec440f5c384e1554e22b28c545dd6ea43b75f1a Mon Sep 17 00:00:00 2001 From: Dilanka96 Date: Wed, 3 May 2023 15:17:52 +0530 Subject: [PATCH 63/94] Fixed formatting --- rasa_sdk/knowledge_base/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rasa_sdk/knowledge_base/utils.py b/rasa_sdk/knowledge_base/utils.py index 2fd60a4a6..24011ff89 100644 --- a/rasa_sdk/knowledge_base/utils.py +++ b/rasa_sdk/knowledge_base/utils.py @@ -175,8 +175,8 @@ def match_extracted_entities_to_object_type( If the user ask a question about an attribute using an object name and without specifying the object type, then this function searches the corresponding object type. (e.g: when user asks'price range of B&B', this - function extracts the object type as 'hotel'). Here we assume that the user message contains reference only to one - object type in the knowledge base. + function extracts the object type as 'hotel'). Here we assume that the user + message contains reference only to one object type in the knowledge base. Args: tracker: the tracker From 1cf92639dbd425b34aba132c42196e39896370cc Mon Sep 17 00:00:00 2001 From: Gajithra Date: Wed, 3 May 2023 15:45:27 +0530 Subject: [PATCH 64/94] added comment about slot_object_type resetting --- rasa_sdk/knowledge_base/actions.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 3821859a5..0c72ad29e 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -199,8 +199,10 @@ async def _query_objects( last_object = None if len(objects) > 1 else objects[0][key_attribute] - # reset the SLOT_OBJECT_TYPE to None. This enables the user to ask about an object attribute - # without first asking to list the objects for an object type in order to set this slot. + # There can be instances where the object type has to be extracted while the action is executed. + # (E.g: ‘what is the price range of Berlin Burrito Company?’). Therefore we need to reset the + # SLOT_OBJECT_TYPE to None to enable this functionality. + slots = [ SlotSet(SLOT_OBJECT_TYPE, None), SlotSet(SLOT_MENTION, None), @@ -268,8 +270,9 @@ async def _query_attribute( dispatcher, object_representation, attribute, value ) ) - # reset the SLOT_OBJECT_TYPE to None. This enables the user to ask about an object attribute - # without first asking to list the objects for an object type in order to set this slot. + # There can be instances where the object type has to be extracted while the action is executed. + # (E.g: ‘what is the price range of Berlin Burrito Company?’). Therefore we need to reset the + # SLOT_OBJECT_TYPE to None to enable this functionality. slots = [ SlotSet(SLOT_OBJECT_TYPE, None), SlotSet(SLOT_ATTRIBUTE, None), From 433991aa84c2765f018148adb9d22ae4224b5759 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Wed, 3 May 2023 15:50:57 +0530 Subject: [PATCH 65/94] formatted code with black --- rasa_sdk/knowledge_base/actions.py | 8 ++++---- tests/knowledge_base/test_actions.py | 13 +++---------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index a4296a1a1..7d655b417 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -199,13 +199,13 @@ async def _query_objects( ) last_object = None if len(objects) > 1 else objects[0][key_attribute] - + # There can be instances where the object type has to be extracted while the action is executed. - # (E.g: ‘what is the price range of Berlin Burrito Company?’). Therefore we need to reset the + # (E.g: ‘what is the price range of Berlin Burrito Company?’). Therefore we need to reset the # SLOT_OBJECT_TYPE to None to enable this functionality. slots = [ - SlotSet(SLOT_OBJECT_TYPE, None), + SlotSet(SLOT_OBJECT_TYPE, None), SlotSet(SLOT_MENTION, None), SlotSet(SLOT_ATTRIBUTE, None), SlotSet(SLOT_LAST_OBJECT, last_object), @@ -272,7 +272,7 @@ async def _query_attribute( ) ) # There can be instances where the object type has to be extracted while the action is executed. - # (E.g: ‘what is the price range of Berlin Burrito Company?’). Therefore we need to reset the + # (E.g: ‘what is the price range of Berlin Burrito Company?’). Therefore we need to reset the # SLOT_OBJECT_TYPE to None to enable this functionality. slots = [ SlotSet(SLOT_OBJECT_TYPE, None), diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index e2b69b275..b193340cb 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -113,7 +113,6 @@ def compare_slots(slot_list_1, slot_list_2): SlotSet(SLOT_LAST_OBJECT_TYPE, "restaurant"), ], ), - ( { "entities": [ @@ -142,7 +141,6 @@ def compare_slots(slot_list_1, slot_list_2): SlotSet(SLOT_LAST_OBJECT_TYPE, "restaurant"), ], ), - ( { "entities": [], @@ -157,7 +155,6 @@ def compare_slots(slot_list_1, slot_list_2): }, [], ), - ], ) async def test_action_run(data_file, latest_message, slots, expected_slots): @@ -200,15 +197,11 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): # Check that temporary slot setting by action is correct. if not any(slots): object_types = knowledge_base.get_object_types() - object_type = match_extracted_entities_to_object_type( - tracker, object_types - ) + object_type = match_extracted_entities_to_object_type(tracker, object_types) set_object_type_slot_event = [SlotSet(SLOT_OBJECT_TYPE, object_type)] - tracker.add_slots( - set_object_type_slot_event - ) + tracker.add_slots(set_object_type_slot_event) if slots.get(SLOT_MENTION) is None: - expected_temp_object_type_value = 'restaurant' + expected_temp_object_type_value = "restaurant" actual_temp_object_type_value = tracker.slots[SLOT_OBJECT_TYPE] assert actual_temp_object_type_value == expected_temp_object_type_value From 32d89acc640299891faaa8cf23db9045df387fbc Mon Sep 17 00:00:00 2001 From: Gajithra Date: Wed, 3 May 2023 15:54:28 +0530 Subject: [PATCH 66/94] fixed typos --- changelog/922.improvement.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/changelog/922.improvement.md b/changelog/922.improvement.md index 2fb7be7aa..b0f0194ea 100644 --- a/changelog/922.improvement.md +++ b/changelog/922.improvement.md @@ -18,12 +18,12 @@ Your input -> what is the price range of Berlin Burrito Company? - The improvement requires changes to the classes ActionQueryKnowledgeBase and InMemoryKnowledgeBase under rasa-sdk. - The `object_type` can be inferred by utilizing the entity extraction (DIET) where object types are used as entities to annotate object names. This also requires changes to be made to slot management to enable dynamic inference of `object_type`. -- The scope of suggested solution is limited to user queries where they ask for an attribute of a given object without mentioning the object type and without having the need to first ask a list of options of corresponding object type. -- E.g: If the user asks ‘price range of Berlin Burrito Company’, then rasa will extract and set attribute slot value to ‘price-range’ and hotel slot value to ‘Berlin Burrito Company’. From this it can be inferred that the user is talking about object type ‘hotel’. +- The scope of the suggested solution is limited to user queries where they ask for an attribute of a given object without mentioning the object type and without needing to first ask for a list of options of the corresponding object type. +- E.g.: If the user asks for ‘price range of Berlin Burrito Company’, then rasa will extract and set attribute slot value to ‘price-range’ and hotel slot value to ‘Berlin Burrito Company’. From this, it can be inferred that the user is talking about the object type ‘hotel’. ## Summary of Changes -- In order to enable the inferance of `object_type` using the entities the following changes were made to existing code base: - - Extract the list of object_types from our knowledge base using a new method `get_object_types()` in `storage.py` for `InMemoryKnowledgeBase` class. +- To enable the inference of `object_type` using the entities the following changes were made to the existing code base: + - Extract the list of object_types from our knowledge base using a new method `get_object_types()` in `storage.py` for the `InMemoryKnowledgeBase` class. - A new method named `match_extracted_entities_to_object_type()` was added in `utils.py` to infer the object type of a given object using the entities and list of object types - - The relevant logic was added in `actions.py` to infer the object type using the above functionalities when object type slot is not set. - - To enable dynamic inference of `object_type`, changes to slot management is also required. Currently, the change made is to reset `object_type` slot to `None` after every conversation turn. \ No newline at end of file + - The relevant logic was added in `actions.py` to infer the object type using the above functionalities when the object type slot is not set. + - To enable dynamic inference of `object_type`, changes to slot management are also required. Currently, the change is to reset the `object_type` slot to `None` after every conversation turn. \ No newline at end of file From c9fae11113698add1ab2569d38f2a56a040b7785 Mon Sep 17 00:00:00 2001 From: Dilanka96 Date: Wed, 3 May 2023 16:39:04 +0530 Subject: [PATCH 67/94] Reformatted actions file --- rasa_sdk/knowledge_base/actions.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 7d655b417..ca2440697 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -200,9 +200,10 @@ async def _query_objects( last_object = None if len(objects) > 1 else objects[0][key_attribute] - # There can be instances where the object type has to be extracted while the action is executed. - # (E.g: ‘what is the price range of Berlin Burrito Company?’). Therefore we need to reset the - # SLOT_OBJECT_TYPE to None to enable this functionality. + # There can be instances where the object type has to be extracted + # while the action is executed.(E.g: ‘what is the price range of Berlin + # Burrito Company?’).Therefore we need to reset the SLOT_OBJECT_TYPE to + # None to enable this functionality. slots = [ SlotSet(SLOT_OBJECT_TYPE, None), @@ -271,9 +272,12 @@ async def _query_attribute( dispatcher, object_representation, attribute, value ) ) - # There can be instances where the object type has to be extracted while the action is executed. - # (E.g: ‘what is the price range of Berlin Burrito Company?’). Therefore we need to reset the - # SLOT_OBJECT_TYPE to None to enable this functionality. + + # There can be instances where the object type has to be extracted + # while the action is executed.(E.g: ‘what is the price range of Berlin + # Burrito Company?’).Therefore we need to reset the SLOT_OBJECT_TYPE to + # None to enable this functionality. + slots = [ SlotSet(SLOT_OBJECT_TYPE, None), SlotSet(SLOT_ATTRIBUTE, None), From 17b1f401ba3d7c0d8934cd88a99db714ca27d27d Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 5 May 2023 10:19:50 +0530 Subject: [PATCH 68/94] resolve lint code issue caused by inverted comma --- rasa_sdk/knowledge_base/actions.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index ca2440697..6b1cbd859 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -201,8 +201,8 @@ async def _query_objects( last_object = None if len(objects) > 1 else objects[0][key_attribute] # There can be instances where the object type has to be extracted - # while the action is executed.(E.g: ‘what is the price range of Berlin - # Burrito Company?’).Therefore we need to reset the SLOT_OBJECT_TYPE to + # while the action is executed.(for example "what is the price range of Berlin + # Burrito Company?").Therefore we need to reset the SLOT_OBJECT_TYPE to # None to enable this functionality. slots = [ @@ -274,8 +274,8 @@ async def _query_attribute( ) # There can be instances where the object type has to be extracted - # while the action is executed.(E.g: ‘what is the price range of Berlin - # Burrito Company?’).Therefore we need to reset the SLOT_OBJECT_TYPE to + # while the action is executed.(for example "what is the price range of Berlin + # Burrito Company?").Therefore we need to reset the SLOT_OBJECT_TYPE to # None to enable this functionality. slots = [ From aa0b218d7a42a4d071e2ab2467fe9ebb68281cd4 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 5 May 2023 19:34:42 +0530 Subject: [PATCH 69/94] added a comment on replacing new_request with has_attribute_in_latest --- rasa_sdk/knowledge_base/actions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 6b1cbd859..593346aea 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -133,6 +133,8 @@ async def run( attribute = tracker.get_slot(SLOT_ATTRIBUTE) has_mention = tracker.get_slot(SLOT_MENTION) is not None + # check if attribute entity is found in latest user message. This is used + # to track whether the request is to query objects or query attributes has_attribute_in_latest = any( entity.get("entity") == "attribute" for entity in tracker.latest_message["entities"] From b414d03e402cdfe13a5c6e4fe402467ec697fe5f Mon Sep 17 00:00:00 2001 From: Gajithra Date: Mon, 8 May 2023 09:47:18 +0530 Subject: [PATCH 70/94] modified doc string in run --- rasa_sdk/knowledge_base/actions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 593346aea..4dbf222df 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -116,9 +116,9 @@ async def run( """ Executes this action. If the user ask a question about an attribute, the knowledge base is queried for that attribute. Otherwise, if no - attribute was detected in the request or the user is talking about a new - object type, multiple objects of the requested type are returned from the - knowledge base. + attribute was detected in the latest request it assumes user is talking + about a new object type and, multiple objects of the requested type are + returned from the knowledge base. Args: dispatcher: the dispatcher From 35faf2bdfd715266c92d0e9490de2512be2bb05a Mon Sep 17 00:00:00 2001 From: Dilanka96 Date: Mon, 8 May 2023 11:38:39 +0530 Subject: [PATCH 71/94] Fixed trailing whitespace issue --- rasa_sdk/knowledge_base/actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 4dbf222df..3609f081e 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -117,7 +117,7 @@ async def run( Executes this action. If the user ask a question about an attribute, the knowledge base is queried for that attribute. Otherwise, if no attribute was detected in the latest request it assumes user is talking - about a new object type and, multiple objects of the requested type are + about a new object type and, multiple objects of the requested type are returned from the knowledge base. Args: From 7c87564244cfb20f251a573a822e513bf72f0fd1 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Mon, 8 May 2023 14:41:26 +0530 Subject: [PATCH 72/94] removed keys from entities payload that are not needed for test_utils.py --- tests/knowledge_base/test_utils.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/tests/knowledge_base/test_utils.py b/tests/knowledge_base/test_utils.py index fe652e261..48b2ad2b2 100644 --- a/tests/knowledge_base/test_utils.py +++ b/tests/knowledge_base/test_utils.py @@ -139,21 +139,10 @@ def test_get_object_name(slots, use_last_object_mention, expected_object_name): { "entities": [ { - "entity": "attribute", - "start": 0, - "end": 11, - "confidence_entity": 0.9997496008872986, - "value": "price-range", - "extractor": "DIETClassifier", - "processors": ["EntitySynonymMapper"], + "entity": "attribute" }, { - "entity": "restaurant", - "start": 15, - "end": 21, - "confidence_entity": 0.9953670501708984, - "value": "Donath", - "extractor": "DIETClassifier", + "entity": "restaurant" }, ], }, From e6b880fbe555428cae02e8a3aec2f213becbec89 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Wed, 17 May 2023 09:50:06 +0530 Subject: [PATCH 73/94] added function docstring in get_object_types() to look up from parent class --- rasa_sdk/knowledge_base/storage.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rasa_sdk/knowledge_base/storage.py b/rasa_sdk/knowledge_base/storage.py index 7c3e3f718..1ca926bbc 100644 --- a/rasa_sdk/knowledge_base/storage.py +++ b/rasa_sdk/knowledge_base/storage.py @@ -251,4 +251,5 @@ async def get_object( return objects_of_interest[0] def get_object_types(self) -> List[Text]: + """See parent class docstring.""" return list(self.data.keys()) From 8a846d787c40296549da2062db0d73e3b7e8df31 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Wed, 17 May 2023 09:50:48 +0530 Subject: [PATCH 74/94] removed repeated info in docstring --- rasa_sdk/knowledge_base/storage.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/rasa_sdk/knowledge_base/storage.py b/rasa_sdk/knowledge_base/storage.py index 1ca926bbc..22f31e853 100644 --- a/rasa_sdk/knowledge_base/storage.py +++ b/rasa_sdk/knowledge_base/storage.py @@ -112,9 +112,6 @@ async def get_object( def get_object_types(self) -> List[Text]: """ Returns a list of object types from knowledge base data. - - Returns: the list of object types - """ raise NotImplementedError("Method is not implemented.") From 2cac3cbc9d8d42dc4c0c7b47f446f69aab134ff8 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Wed, 17 May 2023 09:55:31 +0530 Subject: [PATCH 75/94] modified comment on resetting object type slot based on review suggestion --- rasa_sdk/knowledge_base/actions.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 4dbf222df..bf7a386c1 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -202,9 +202,9 @@ async def _query_objects( last_object = None if len(objects) > 1 else objects[0][key_attribute] - # There can be instances where the object type has to be extracted - # while the action is executed.(for example "what is the price range of Berlin - # Burrito Company?").Therefore we need to reset the SLOT_OBJECT_TYPE to + # To prevent the user to first ask to list the objects for an object type, + # the object type has to be extracted while the action is executed. + # Therefore we need to reset the SLOT_OBJECT_TYPE to # None to enable this functionality. slots = [ @@ -275,9 +275,9 @@ async def _query_attribute( ) ) - # There can be instances where the object type has to be extracted - # while the action is executed.(for example "what is the price range of Berlin - # Burrito Company?").Therefore we need to reset the SLOT_OBJECT_TYPE to + # To prevent the user to first ask to list the objects for an object type, + # the object type has to be extracted while the action is executed. + # Therefore we need to reset the SLOT_OBJECT_TYPE to # None to enable this functionality. slots = [ From f4026332195c8424d2293ff7b3adf08ad5ca6ec0 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Wed, 17 May 2023 09:56:51 +0530 Subject: [PATCH 76/94] replaced has_attribute_in_latest with has_attribute_in_latest_message --- rasa_sdk/knowledge_base/actions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index bf7a386c1..3b8d08d79 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -135,7 +135,7 @@ async def run( # check if attribute entity is found in latest user message. This is used # to track whether the request is to query objects or query attributes - has_attribute_in_latest = any( + has_attribute_in_latest_message = any( entity.get("entity") == "attribute" for entity in tracker.latest_message["entities"] ) @@ -150,7 +150,7 @@ async def run( set_object_type_slot_event ) # temporarily set the `object_type_slot` to extracted value - if object_type and not has_attribute_in_latest: + if object_type and not has_attribute_in_latest_message: return await self._query_objects(dispatcher, object_type, tracker) elif object_type and attribute: return await self._query_attribute( From 097de252319a831bce817e659680c4db62259eab Mon Sep 17 00:00:00 2001 From: Dilanka96 Date: Wed, 17 May 2023 12:03:33 +0530 Subject: [PATCH 77/94] Fixed linting issues --- rasa_sdk/knowledge_base/actions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rasa_sdk/knowledge_base/actions.py b/rasa_sdk/knowledge_base/actions.py index 7471d36cb..097bd5031 100644 --- a/rasa_sdk/knowledge_base/actions.py +++ b/rasa_sdk/knowledge_base/actions.py @@ -202,7 +202,7 @@ async def _query_objects( last_object = None if len(objects) > 1 else objects[0][key_attribute] - # To prevent the user to first ask to list the objects for an object type, + # To prevent the user to first ask to list the objects for an object type, # the object type has to be extracted while the action is executed. # Therefore we need to reset the SLOT_OBJECT_TYPE to # None to enable this functionality. @@ -275,7 +275,7 @@ async def _query_attribute( ) ) - # To prevent the user to first ask to list the objects for an object type, + # To prevent the user to first ask to list the objects for an object type, # the object type has to be extracted while the action is executed. # Therefore we need to reset the SLOT_OBJECT_TYPE to # None to enable this functionality. From ffb581895345e68cdc168d351cc85c7b2f7e1778 Mon Sep 17 00:00:00 2001 From: Dilanka96 Date: Wed, 17 May 2023 12:13:21 +0530 Subject: [PATCH 78/94] Reformatted using black --- tests/knowledge_base/test_utils.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/knowledge_base/test_utils.py b/tests/knowledge_base/test_utils.py index 48b2ad2b2..e5438939d 100644 --- a/tests/knowledge_base/test_utils.py +++ b/tests/knowledge_base/test_utils.py @@ -138,12 +138,8 @@ def test_get_object_name(slots, use_last_object_mention, expected_object_name): ( { "entities": [ - { - "entity": "attribute" - }, - { - "entity": "restaurant" - }, + {"entity": "attribute"}, + {"entity": "restaurant"}, ], }, ["hotel", "restaurant"], From 3048e5b298d1d323f4fc20236f1452cd52cd4dea Mon Sep 17 00:00:00 2001 From: Gajithra Date: Wed, 17 May 2023 22:54:02 +0530 Subject: [PATCH 79/94] added a rough code on testing the tracker events --- tests/knowledge_base/test_actions.py | 38 +++++++++++++++------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index b193340cb..bd5957acc 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -14,7 +14,7 @@ SLOT_LAST_OBJECT_TYPE, ) from rasa_sdk.knowledge_base.utils import match_extracted_entities_to_object_type - +import logging def compare_slots(slot_list_1, slot_list_2): assert len(slot_list_2) == len(slot_list_1) @@ -154,7 +154,7 @@ def compare_slots(slot_list_1, slot_list_2): SLOT_LAST_OBJECT_TYPE: None, }, [], - ), + ), ], ) async def test_action_run(data_file, latest_message, slots, expected_slots): @@ -166,7 +166,9 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): tracker = Tracker( "default", slots, latest_message, [], False, None, {}, "action_listen" ) + # print(f"Before run actions:{tracker.events}") actual_slots = await action.run(dispatcher, tracker, {}) + compare_slots(expected_slots, actual_slots) compare_slots(actual_slots, expected_slots) @@ -195,18 +197,20 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): assert actual_msg == expected_msg # Check that temporary slot setting by action is correct. - if not any(slots): - object_types = knowledge_base.get_object_types() - object_type = match_extracted_entities_to_object_type(tracker, object_types) - set_object_type_slot_event = [SlotSet(SLOT_OBJECT_TYPE, object_type)] - tracker.add_slots(set_object_type_slot_event) - if slots.get(SLOT_MENTION) is None: - expected_temp_object_type_value = "restaurant" - actual_temp_object_type_value = tracker.slots[SLOT_OBJECT_TYPE] - - assert actual_temp_object_type_value == expected_temp_object_type_value - else: - expected_temp_object_type_value = None - actual_temp_object_type_value = tracker.slots[SLOT_OBJECT_TYPE] - - assert actual_temp_object_type_value == expected_temp_object_type_value + print(f"slots:{slots}") + # if not any(slots): + if any(value is not None for value in slots.values()): + print("=========================== LINE 204 ======================================") + if slots.get(SLOT_OBJECT_TYPE) is None and slots.get(SLOT_MENTION) is None: + print("=========================== LINE 206 ======================================") + expected_tracker_event = [{'event': 'slot', 'timestamp': None, 'name': 'object_type', 'value': 'restaurant'}] + actual_tracker_event = tracker.events + assert actual_tracker_event==expected_tracker_event + elif slots.get(SLOT_OBJECT_TYPE) is None and slots.get(SLOT_MENTION) is not None: + print("=========================== LINE 210 ======================================") + expected_tracker_event = [{'event': 'slot', 'timestamp': None, 'name': 'object_type', 'value': None}] + actual_tracker_event = tracker.events + # print(f"expected_slots:{expected_slots}") + # print(f"actual_tracker_event:{actual_tracker_event}") + # print(f"expected_tracker_event:{expected_tracker_event}") + assert actual_tracker_event==expected_tracker_event From 7bacf54fe6476d25b8c240f1aef0f8661b3c3960 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Thu, 18 May 2023 10:11:56 +0530 Subject: [PATCH 80/94] removed import logging --- tests/knowledge_base/test_actions.py | 37 ++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index bd5957acc..b0e665d20 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -14,7 +14,6 @@ SLOT_LAST_OBJECT_TYPE, ) from rasa_sdk.knowledge_base.utils import match_extracted_entities_to_object_type -import logging def compare_slots(slot_list_1, slot_list_2): assert len(slot_list_2) == len(slot_list_1) @@ -141,6 +140,34 @@ def compare_slots(slot_list_1, slot_list_2): SlotSet(SLOT_LAST_OBJECT_TYPE, "restaurant"), ], ), + # ( + # { + # "entities": [ + # { + # "entity": "attribute", + # }, + # { + # "entity": "restaurant", + # }, + # ], + # }, + # { + # SLOT_MENTION: None, + # SLOT_ATTRIBUTE: "cuisine", + # SLOT_OBJECT_TYPE: None, + # SLOT_LISTED_OBJECTS: [1, 2, 3], + # SLOT_LAST_OBJECT: None, + # SLOT_LAST_OBJECT_TYPE: "restaurant", + # "restaurant": "PastaBar", + # }, + # [ + # SlotSet(SLOT_MENTION, None), + # SlotSet(SLOT_ATTRIBUTE, None), + # SlotSet(SLOT_OBJECT_TYPE, None), + # SlotSet(SLOT_LAST_OBJECT, 1), + # SlotSet(SLOT_LAST_OBJECT_TYPE, "restaurant"), + # ], + # ), ( { "entities": [], @@ -166,12 +193,13 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): tracker = Tracker( "default", slots, latest_message, [], False, None, {}, "action_listen" ) - # print(f"Before run actions:{tracker.events}") + print(f"\ntest_slots:{slots}") actual_slots = await action.run(dispatcher, tracker, {}) compare_slots(expected_slots, actual_slots) compare_slots(actual_slots, expected_slots) + # Check that utterances produced by action are correct. if slots[SLOT_ATTRIBUTE]: if slots.get("restaurant") is not None: @@ -197,10 +225,9 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): assert actual_msg == expected_msg # Check that temporary slot setting by action is correct. - print(f"slots:{slots}") # if not any(slots): if any(value is not None for value in slots.values()): - print("=========================== LINE 204 ======================================") + # print(f"\ntest_slots:{slots}") if slots.get(SLOT_OBJECT_TYPE) is None and slots.get(SLOT_MENTION) is None: print("=========================== LINE 206 ======================================") expected_tracker_event = [{'event': 'slot', 'timestamp': None, 'name': 'object_type', 'value': 'restaurant'}] @@ -213,4 +240,4 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): # print(f"expected_slots:{expected_slots}") # print(f"actual_tracker_event:{actual_tracker_event}") # print(f"expected_tracker_event:{expected_tracker_event}") - assert actual_tracker_event==expected_tracker_event + assert actual_tracker_event==expected_tracker_event \ No newline at end of file From afa3b48ea9c8c01e726f26de4eb6937335f8bc29 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Thu, 18 May 2023 10:25:26 +0530 Subject: [PATCH 81/94] added print statements for testing --- tests/knowledge_base/test_actions.py | 32 ++-------------------------- 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index b0e665d20..ed88680af 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -140,34 +140,6 @@ def compare_slots(slot_list_1, slot_list_2): SlotSet(SLOT_LAST_OBJECT_TYPE, "restaurant"), ], ), - # ( - # { - # "entities": [ - # { - # "entity": "attribute", - # }, - # { - # "entity": "restaurant", - # }, - # ], - # }, - # { - # SLOT_MENTION: None, - # SLOT_ATTRIBUTE: "cuisine", - # SLOT_OBJECT_TYPE: None, - # SLOT_LISTED_OBJECTS: [1, 2, 3], - # SLOT_LAST_OBJECT: None, - # SLOT_LAST_OBJECT_TYPE: "restaurant", - # "restaurant": "PastaBar", - # }, - # [ - # SlotSet(SLOT_MENTION, None), - # SlotSet(SLOT_ATTRIBUTE, None), - # SlotSet(SLOT_OBJECT_TYPE, None), - # SlotSet(SLOT_LAST_OBJECT, 1), - # SlotSet(SLOT_LAST_OBJECT_TYPE, "restaurant"), - # ], - # ), ( { "entities": [], @@ -193,7 +165,7 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): tracker = Tracker( "default", slots, latest_message, [], False, None, {}, "action_listen" ) - print(f"\ntest_slots:{slots}") + actual_slots = await action.run(dispatcher, tracker, {}) compare_slots(expected_slots, actual_slots) @@ -227,7 +199,7 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): # Check that temporary slot setting by action is correct. # if not any(slots): if any(value is not None for value in slots.values()): - # print(f"\ntest_slots:{slots}") + print(f"\ntest_slots:{slots}") if slots.get(SLOT_OBJECT_TYPE) is None and slots.get(SLOT_MENTION) is None: print("=========================== LINE 206 ======================================") expected_tracker_event = [{'event': 'slot', 'timestamp': None, 'name': 'object_type', 'value': 'restaurant'}] From ff1a1c4407a34000bee37347934268e3c826a708 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 19 May 2023 15:04:51 +0530 Subject: [PATCH 82/94] resolved python mutable issue --- tests/knowledge_base/test_actions.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index ed88680af..33583ca22 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -14,6 +14,7 @@ SLOT_LAST_OBJECT_TYPE, ) from rasa_sdk.knowledge_base.utils import match_extracted_entities_to_object_type +import copy def compare_slots(slot_list_1, slot_list_2): assert len(slot_list_2) == len(slot_list_1) @@ -166,6 +167,11 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): "default", slots, latest_message, [], False, None, {}, "action_listen" ) + # To prevent unintended modifications, create a copy of the slots dictionary + # before passing it to the Tracker object, as dictionaries in + # Python are mutable and passed by reference. + initial_slots = dict(slots) + actual_slots = await action.run(dispatcher, tracker, {}) compare_slots(expected_slots, actual_slots) @@ -197,19 +203,15 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): assert actual_msg == expected_msg # Check that temporary slot setting by action is correct. - # if not any(slots): - if any(value is not None for value in slots.values()): - print(f"\ntest_slots:{slots}") - if slots.get(SLOT_OBJECT_TYPE) is None and slots.get(SLOT_MENTION) is None: + # if not any(initial_slots): + if any(value is not None for value in initial_slots.values()): + if initial_slots.get(SLOT_OBJECT_TYPE) is None and initial_slots.get(SLOT_MENTION) is None: print("=========================== LINE 206 ======================================") expected_tracker_event = [{'event': 'slot', 'timestamp': None, 'name': 'object_type', 'value': 'restaurant'}] actual_tracker_event = tracker.events assert actual_tracker_event==expected_tracker_event - elif slots.get(SLOT_OBJECT_TYPE) is None and slots.get(SLOT_MENTION) is not None: + elif initial_slots.get(SLOT_OBJECT_TYPE) is None and initial_slots.get(SLOT_MENTION) is not None: print("=========================== LINE 210 ======================================") expected_tracker_event = [{'event': 'slot', 'timestamp': None, 'name': 'object_type', 'value': None}] actual_tracker_event = tracker.events - # print(f"expected_slots:{expected_slots}") - # print(f"actual_tracker_event:{actual_tracker_event}") - # print(f"expected_tracker_event:{expected_tracker_event}") assert actual_tracker_event==expected_tracker_event \ No newline at end of file From acfe716b46797829910b63478eb269e91228d332 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 19 May 2023 15:15:16 +0530 Subject: [PATCH 83/94] removed print statements and cleaned the code --- tests/knowledge_base/test_actions.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index 33583ca22..322d07de1 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -203,15 +203,12 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): assert actual_msg == expected_msg # Check that temporary slot setting by action is correct. - # if not any(initial_slots): - if any(value is not None for value in initial_slots.values()): + if any(initial_slots.values()): if initial_slots.get(SLOT_OBJECT_TYPE) is None and initial_slots.get(SLOT_MENTION) is None: - print("=========================== LINE 206 ======================================") expected_tracker_event = [{'event': 'slot', 'timestamp': None, 'name': 'object_type', 'value': 'restaurant'}] actual_tracker_event = tracker.events assert actual_tracker_event==expected_tracker_event elif initial_slots.get(SLOT_OBJECT_TYPE) is None and initial_slots.get(SLOT_MENTION) is not None: - print("=========================== LINE 210 ======================================") expected_tracker_event = [{'event': 'slot', 'timestamp': None, 'name': 'object_type', 'value': None}] actual_tracker_event = tracker.events assert actual_tracker_event==expected_tracker_event \ No newline at end of file From 0527396c5ff891b0879312ff07a9022ea5f3265e Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 19 May 2023 15:19:48 +0530 Subject: [PATCH 84/94] removed import copy --- tests/knowledge_base/test_actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index 322d07de1..31977cd0b 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -14,7 +14,7 @@ SLOT_LAST_OBJECT_TYPE, ) from rasa_sdk.knowledge_base.utils import match_extracted_entities_to_object_type -import copy + def compare_slots(slot_list_1, slot_list_2): assert len(slot_list_2) == len(slot_list_1) From 7d7a4e88291bfb9b341ab7e17b9ed998e4b0a3dd Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 19 May 2023 15:21:08 +0530 Subject: [PATCH 85/94] removed trailing white space in line 157 --- tests/knowledge_base/test_actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index 31977cd0b..7d1c4b746 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -154,7 +154,7 @@ def compare_slots(slot_list_1, slot_list_2): SLOT_LAST_OBJECT_TYPE: None, }, [], - ), + ), ], ) async def test_action_run(data_file, latest_message, slots, expected_slots): From 6442349a1f290b659bf9f4626b742a9292ebc52c Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 19 May 2023 15:21:54 +0530 Subject: [PATCH 86/94] removed trailing white space in line 157 --- tests/knowledge_base/test_actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index 7d1c4b746..fdd14556d 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -154,7 +154,7 @@ def compare_slots(slot_list_1, slot_list_2): SLOT_LAST_OBJECT_TYPE: None, }, [], - ), + ), ], ) async def test_action_run(data_file, latest_message, slots, expected_slots): From 118b4be822f2afa7bb29b24b9ad03e5dca1956cf Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 19 May 2023 15:23:14 +0530 Subject: [PATCH 87/94] added spaces between code lines --- tests/knowledge_base/test_actions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index fdd14556d..ca2fc169c 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -207,8 +207,10 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): if initial_slots.get(SLOT_OBJECT_TYPE) is None and initial_slots.get(SLOT_MENTION) is None: expected_tracker_event = [{'event': 'slot', 'timestamp': None, 'name': 'object_type', 'value': 'restaurant'}] actual_tracker_event = tracker.events + assert actual_tracker_event==expected_tracker_event elif initial_slots.get(SLOT_OBJECT_TYPE) is None and initial_slots.get(SLOT_MENTION) is not None: expected_tracker_event = [{'event': 'slot', 'timestamp': None, 'name': 'object_type', 'value': None}] actual_tracker_event = tracker.events + assert actual_tracker_event==expected_tracker_event \ No newline at end of file From 7e7bda06bb8cbb60f1fa389e781f56a49df4a367 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 19 May 2023 15:24:29 +0530 Subject: [PATCH 88/94] added spaces between code lines --- tests/knowledge_base/test_actions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index ca2fc169c..5f96254a8 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -209,8 +209,9 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): actual_tracker_event = tracker.events assert actual_tracker_event==expected_tracker_event + elif initial_slots.get(SLOT_OBJECT_TYPE) is None and initial_slots.get(SLOT_MENTION) is not None: expected_tracker_event = [{'event': 'slot', 'timestamp': None, 'name': 'object_type', 'value': None}] actual_tracker_event = tracker.events - + assert actual_tracker_event==expected_tracker_event \ No newline at end of file From f4e88d7d34ec1bbe5046c936026251f672e53cc8 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Fri, 19 May 2023 15:25:31 +0530 Subject: [PATCH 89/94] added space --- tests/knowledge_base/test_utils.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/knowledge_base/test_utils.py b/tests/knowledge_base/test_utils.py index e5438939d..48b2ad2b2 100644 --- a/tests/knowledge_base/test_utils.py +++ b/tests/knowledge_base/test_utils.py @@ -138,8 +138,12 @@ def test_get_object_name(slots, use_last_object_mention, expected_object_name): ( { "entities": [ - {"entity": "attribute"}, - {"entity": "restaurant"}, + { + "entity": "attribute" + }, + { + "entity": "restaurant" + }, ], }, ["hotel", "restaurant"], From e653150d275dabd3393cbba7e9e10c344455973e Mon Sep 17 00:00:00 2001 From: Dilanka96 Date: Fri, 19 May 2023 15:57:17 +0530 Subject: [PATCH 90/94] Fixed formatting and lint code --- tests/knowledge_base/test_actions.py | 40 ++++++++++++++++++++-------- tests/knowledge_base/test_utils.py | 8 ++---- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index 5f96254a8..e2d24a088 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -13,7 +13,6 @@ SLOT_LAST_OBJECT, SLOT_LAST_OBJECT_TYPE, ) -from rasa_sdk.knowledge_base.utils import match_extracted_entities_to_object_type def compare_slots(slot_list_1, slot_list_2): @@ -167,8 +166,8 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): "default", slots, latest_message, [], False, None, {}, "action_listen" ) - # To prevent unintended modifications, create a copy of the slots dictionary - # before passing it to the Tracker object, as dictionaries in + # To prevent unintended modifications, create a copy of the slots dictionary + # before passing it to the Tracker object, as dictionaries in # Python are mutable and passed by reference. initial_slots = dict(slots) @@ -177,7 +176,6 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): compare_slots(expected_slots, actual_slots) compare_slots(actual_slots, expected_slots) - # Check that utterances produced by action are correct. if slots[SLOT_ATTRIBUTE]: if slots.get("restaurant") is not None: @@ -204,14 +202,34 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): # Check that temporary slot setting by action is correct. if any(initial_slots.values()): - if initial_slots.get(SLOT_OBJECT_TYPE) is None and initial_slots.get(SLOT_MENTION) is None: - expected_tracker_event = [{'event': 'slot', 'timestamp': None, 'name': 'object_type', 'value': 'restaurant'}] + if ( + initial_slots.get(SLOT_OBJECT_TYPE) is None + and initial_slots.get(SLOT_MENTION) is None + ): + expected_tracker_event = [ + { + "event": "slot", + "timestamp": None, + "name": "object_type", + "value": "restaurant", + } + ] actual_tracker_event = tracker.events - assert actual_tracker_event==expected_tracker_event - - elif initial_slots.get(SLOT_OBJECT_TYPE) is None and initial_slots.get(SLOT_MENTION) is not None: - expected_tracker_event = [{'event': 'slot', 'timestamp': None, 'name': 'object_type', 'value': None}] + assert actual_tracker_event == expected_tracker_event + + elif ( + initial_slots.get(SLOT_OBJECT_TYPE) is None + and initial_slots.get(SLOT_MENTION) is not None + ): + expected_tracker_event = [ + { + "event": "slot", + "timestamp": None, + "name": "object_type", + "value": None, + } + ] actual_tracker_event = tracker.events - assert actual_tracker_event==expected_tracker_event \ No newline at end of file + assert actual_tracker_event == expected_tracker_event diff --git a/tests/knowledge_base/test_utils.py b/tests/knowledge_base/test_utils.py index 48b2ad2b2..e5438939d 100644 --- a/tests/knowledge_base/test_utils.py +++ b/tests/knowledge_base/test_utils.py @@ -138,12 +138,8 @@ def test_get_object_name(slots, use_last_object_mention, expected_object_name): ( { "entities": [ - { - "entity": "attribute" - }, - { - "entity": "restaurant" - }, + {"entity": "attribute"}, + {"entity": "restaurant"}, ], }, ["hotel", "restaurant"], From 592b90deef4d9ec6b9c4aa00faa082b50f7d1b7c Mon Sep 17 00:00:00 2001 From: Gajithra Date: Mon, 5 Jun 2023 14:43:15 +0530 Subject: [PATCH 91/94] made a copy of slots using copy.deepcopy() | modified assertion to check inside actual event list --- tests/knowledge_base/test_actions.py | 41 ++++++++++++---------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index e2d24a088..3a000fd13 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -1,4 +1,5 @@ import pytest +import copy from rasa_sdk import Tracker from rasa_sdk.events import SlotSet @@ -169,7 +170,7 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): # To prevent unintended modifications, create a copy of the slots dictionary # before passing it to the Tracker object, as dictionaries in # Python are mutable and passed by reference. - initial_slots = dict(slots) + initial_slots = copy.deepcopy(slots) actual_slots = await action.run(dispatcher, tracker, {}) @@ -206,30 +207,24 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): initial_slots.get(SLOT_OBJECT_TYPE) is None and initial_slots.get(SLOT_MENTION) is None ): - expected_tracker_event = [ - { - "event": "slot", - "timestamp": None, - "name": "object_type", - "value": "restaurant", - } - ] - actual_tracker_event = tracker.events - - assert actual_tracker_event == expected_tracker_event + expected_tracker_event = { + "event": "slot", + "timestamp": None, + "name": "object_type", + "value": "restaurant", + } + + assert expected_tracker_event in tracker.events elif ( initial_slots.get(SLOT_OBJECT_TYPE) is None and initial_slots.get(SLOT_MENTION) is not None ): - expected_tracker_event = [ - { - "event": "slot", - "timestamp": None, - "name": "object_type", - "value": None, - } - ] - actual_tracker_event = tracker.events - - assert actual_tracker_event == expected_tracker_event + expected_tracker_event = { + "event": "slot", + "timestamp": None, + "name": "object_type", + "value": None, + } + + assert expected_tracker_event in tracker.events From b1a54890a70bc083aa0ca46af1f70f41e56936bc Mon Sep 17 00:00:00 2001 From: Gajithra Date: Mon, 5 Jun 2023 14:44:40 +0530 Subject: [PATCH 92/94] format with black --- tests/knowledge_base/test_actions.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index 3a000fd13..f8c1aa628 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -208,12 +208,12 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): and initial_slots.get(SLOT_MENTION) is None ): expected_tracker_event = { - "event": "slot", - "timestamp": None, - "name": "object_type", - "value": "restaurant", - } - + "event": "slot", + "timestamp": None, + "name": "object_type", + "value": "restaurant", + } + assert expected_tracker_event in tracker.events elif ( @@ -221,10 +221,10 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): and initial_slots.get(SLOT_MENTION) is not None ): expected_tracker_event = { - "event": "slot", - "timestamp": None, - "name": "object_type", - "value": None, - } - - assert expected_tracker_event in tracker.events + "event": "slot", + "timestamp": None, + "name": "object_type", + "value": None, + } + + assert expected_tracker_event in tracker.events From 75477638ea3a5fc5936dfd11c1b27eb6c302b981 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Mon, 5 Jun 2023 16:59:39 +0530 Subject: [PATCH 93/94] added comments in tracker event assertion code block --- tests/knowledge_base/test_actions.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index f8c1aa628..d41f70f14 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -203,10 +203,16 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): # Check that temporary slot setting by action is correct. if any(initial_slots.values()): + # The condition block below checks for user message + # such as "what is the price range of Pasta Bar?". + # This user message example is denoted by test case `4`. if ( initial_slots.get(SLOT_OBJECT_TYPE) is None and initial_slots.get(SLOT_MENTION) is None ): + # Since Pasta Bar belongs to `restaurant` object type + # the tracker event passed to set the slot temporarily + # should look like this. expected_tracker_event = { "event": "slot", "timestamp": None, @@ -215,11 +221,18 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): } assert expected_tracker_event in tracker.events - + + # The condition block below checks for user message + # such as "what is the cuisine of second one?". + # This user message example is denoted by test case `3`. elif ( initial_slots.get(SLOT_OBJECT_TYPE) is None and initial_slots.get(SLOT_MENTION) is not None ): + # Since there is no `restaurant` entity in the user message, + # the `object_type` will be None. + # Therefore, the tracker event passed to set the slot temporarily + # should look like this. expected_tracker_event = { "event": "slot", "timestamp": None, From f8ac1bf38022fbc1bfbc09abca65b1157fcdbbe3 Mon Sep 17 00:00:00 2001 From: Gajithra Date: Mon, 5 Jun 2023 17:33:34 +0530 Subject: [PATCH 94/94] formatted code --- tests/knowledge_base/test_actions.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/knowledge_base/test_actions.py b/tests/knowledge_base/test_actions.py index d41f70f14..b962ac70e 100644 --- a/tests/knowledge_base/test_actions.py +++ b/tests/knowledge_base/test_actions.py @@ -212,7 +212,7 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): ): # Since Pasta Bar belongs to `restaurant` object type # the tracker event passed to set the slot temporarily - # should look like this. + # should look like this. expected_tracker_event = { "event": "slot", "timestamp": None, @@ -221,7 +221,7 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): } assert expected_tracker_event in tracker.events - + # The condition block below checks for user message # such as "what is the cuisine of second one?". # This user message example is denoted by test case `3`. @@ -230,9 +230,9 @@ async def test_action_run(data_file, latest_message, slots, expected_slots): and initial_slots.get(SLOT_MENTION) is not None ): # Since there is no `restaurant` entity in the user message, - # the `object_type` will be None. + # the `object_type` will be `None`. # Therefore, the tracker event passed to set the slot temporarily - # should look like this. + # should look like this. expected_tracker_event = { "event": "slot", "timestamp": None,