From 83af06c3db79fcf442ab48fe61b1edf20c20a902 Mon Sep 17 00:00:00 2001 From: Mahesh Date: Fri, 5 Jan 2024 11:48:24 +0530 Subject: [PATCH 1/5] Get whatsapp flow details from webhook. --- kairon/chat/handlers/channels/whatsapp.py | 8 ++- tests/integration_test/chat_service_test.py | 77 +++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/kairon/chat/handlers/channels/whatsapp.py b/kairon/chat/handlers/channels/whatsapp.py index 2239af335..7a5c1212b 100644 --- a/kairon/chat/handlers/channels/whatsapp.py +++ b/kairon/chat/handlers/channels/whatsapp.py @@ -40,7 +40,13 @@ async def message( # so quick reply should be checked first if message.get("type") == "interactive": interactive_type = message.get("interactive").get("type") - text = message["interactive"][interactive_type]["id"] + if interactive_type == "nfm_reply": + response_json = json.dumps( + {interactive_type: message["interactive"][interactive_type]['response_json']} + ) + text = f"/k_flow_msg{response_json}" + else: + text = message["interactive"][interactive_type]["id"] elif message.get("type") == "text": text = message["text"]['body'] elif message.get("type") == "button": diff --git a/tests/integration_test/chat_service_test.py b/tests/integration_test/chat_service_test.py index 94341df3d..3958ee715 100644 --- a/tests/integration_test/chat_service_test.py +++ b/tests/integration_test/chat_service_test.py @@ -1591,6 +1591,83 @@ def _mock_validate_hub_signature(*args, **kwargs): assert whatsapp_msg_handler.call_args[0][4] == bot +@responses.activate +def test_whatsapp_valid_flows_message_request(): + responses.reset() + def _mock_validate_hub_signature(*args, **kwargs): + return True + + with patch.object(MessengerHandler, "validate_hub_signature", _mock_validate_hub_signature): + with mock.patch("kairon.chat.handlers.channels.whatsapp.Whatsapp._handle_user_message", + autospec=True) as whatsapp_msg_handler: + request_json = { + 'object': 'whatsapp_business_account', + 'entry': [{ + 'id': '147142368486217', + 'changes': [{ + 'value': { + 'messaging_product': 'whatsapp', + 'metadata': { + 'display_phone_number': '918657011111', + 'phone_number_id': '142427035629239' + }, + 'contacts': [{ + 'profile': { + 'name': 'Mahesh' + }, + 'wa_id': '919515991111' + }], + 'messages': [{ + 'context': { + 'from': '918657011111', + 'id': 'wamid.HBgMOTE5NTE1OTkxNjg1FQIAERgSMjVGRjYwODI3RkMyOEQ0NUM1AA==' + }, + 'from': '919515991111', + 'id': 'wamid.HBgMOTE5NTE1OTkxNjg1FQIAEhggQTRBQUYyODNBQkMwNEIzRDQ0MUI1ODkyMTE2NTMA', + 'timestamp': '1703257297', + 'type': 'interactive', + 'interactive': { + 'type': 'nfm_reply', + 'nfm_reply': { + 'response_json': '{"flow_token":"AQBBBBBCS5FpgQ_cAAAAAD0QI3s.","firstName":"Mahesh ","lastName":"Sattala ","pincode":"523456","district":"Bangalore ","houseNumber":"5-6","dateOfBirth":"1703257240046","source":"SOCIAL_MEDIA","landmark":"HSR Layout ","email":"maheshsattala@gmail.com"}', + 'body': 'Sent', + 'name': 'flow' + } + } + }] + }, + 'field': 'messages' + }] + }] + } + response = client.post( + f"/api/bot/whatsapp/{bot}/{token}", + headers={"hub.verify_token": "valid"}, + json=request_json + ) + actual = response.json() + assert actual == 'success' + assert len(whatsapp_msg_handler.call_args[0]) == 5 + print(whatsapp_msg_handler.call_args[0][1]) + assert whatsapp_msg_handler.call_args[0][1] == '/k_flow_msg{"nfm_reply": "{\\"flow_token\\":\\"AQBBBBBCS5FpgQ_cAAAAAD0QI3s.\\",\\"firstName\\":\\"Mahesh \\",\\"lastName\\":\\"Sattala \\",\\"pincode\\":\\"523456\\",\\"district\\":\\"Bangalore \\",\\"houseNumber\\":\\"5-6\\",\\"dateOfBirth\\":\\"1703257240046\\",\\"source\\":\\"SOCIAL_MEDIA\\",\\"landmark\\":\\"HSR Layout \\",\\"email\\":\\"maheshsattala@gmail.com\\"}"}' + assert whatsapp_msg_handler.call_args[0][2] == '919515991111' + metadata = whatsapp_msg_handler.call_args[0][3] + metadata.pop("timestamp") + print(metadata) + assert metadata == { + 'context': {'from': '918657011111', 'id': 'wamid.HBgMOTE5NTE1OTkxNjg1FQIAERgSMjVGRjYwODI3RkMyOEQ0NUM1AA=='}, + 'from': '919515991111', 'id': 'wamid.HBgMOTE5NTE1OTkxNjg1FQIAEhggQTRBQUYyODNBQkMwNEIzRDQ0MUI1ODkyMTE2NTMA', + 'type': 'interactive', + 'interactive': { + 'type': 'nfm_reply', 'nfm_reply': { + 'response_json': '{"flow_token":"AQBBBBBCS5FpgQ_cAAAAAD0QI3s.","firstName":"Mahesh ","lastName":"Sattala ","pincode":"523456","district":"Bangalore ","houseNumber":"5-6","dateOfBirth":"1703257240046","source":"SOCIAL_MEDIA","landmark":"HSR Layout ","email":"maheshsattala@gmail.com"}', + 'body': 'Sent', 'name': 'flow'}}, + 'is_integration_user': True, 'bot': bot, 'account': 1, 'channel_type': 'whatsapp', + 'bsp_type': 'meta', 'tabname': 'default', 'display_phone_number': '918657011111', + 'phone_number_id': '142427035629239'} + assert whatsapp_msg_handler.call_args[0][4] == bot + + @responses.activate def test_whatsapp_valid_statuses_with_sent_request(): from kairon.shared.chat.data_objects import ChannelLogs From 8533ea7ef1caa7d274c239f3d67638263992510b Mon Sep 17 00:00:00 2001 From: Mahesh Date: Sat, 6 Jan 2024 17:16:01 +0530 Subject: [PATCH 2/5] Added code to Get whatsapp flow details from webhook. --- kairon/chat/handlers/channels/whatsapp.py | 2 +- tests/integration_test/chat_service_test.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/kairon/chat/handlers/channels/whatsapp.py b/kairon/chat/handlers/channels/whatsapp.py index 7a5c1212b..2e83b7612 100644 --- a/kairon/chat/handlers/channels/whatsapp.py +++ b/kairon/chat/handlers/channels/whatsapp.py @@ -42,7 +42,7 @@ async def message( interactive_type = message.get("interactive").get("type") if interactive_type == "nfm_reply": response_json = json.dumps( - {interactive_type: message["interactive"][interactive_type]['response_json']} + {interactive_type: json.loads(message["interactive"][interactive_type]['response_json'])} ) text = f"/k_flow_msg{response_json}" else: diff --git a/tests/integration_test/chat_service_test.py b/tests/integration_test/chat_service_test.py index 3958ee715..2eda59e86 100644 --- a/tests/integration_test/chat_service_test.py +++ b/tests/integration_test/chat_service_test.py @@ -1594,6 +1594,7 @@ def _mock_validate_hub_signature(*args, **kwargs): @responses.activate def test_whatsapp_valid_flows_message_request(): responses.reset() + def _mock_validate_hub_signature(*args, **kwargs): return True @@ -1648,12 +1649,10 @@ def _mock_validate_hub_signature(*args, **kwargs): actual = response.json() assert actual == 'success' assert len(whatsapp_msg_handler.call_args[0]) == 5 - print(whatsapp_msg_handler.call_args[0][1]) - assert whatsapp_msg_handler.call_args[0][1] == '/k_flow_msg{"nfm_reply": "{\\"flow_token\\":\\"AQBBBBBCS5FpgQ_cAAAAAD0QI3s.\\",\\"firstName\\":\\"Mahesh \\",\\"lastName\\":\\"Sattala \\",\\"pincode\\":\\"523456\\",\\"district\\":\\"Bangalore \\",\\"houseNumber\\":\\"5-6\\",\\"dateOfBirth\\":\\"1703257240046\\",\\"source\\":\\"SOCIAL_MEDIA\\",\\"landmark\\":\\"HSR Layout \\",\\"email\\":\\"maheshsattala@gmail.com\\"}"}' + assert whatsapp_msg_handler.call_args[0][1] == '/k_flow_msg{\"nfm_reply\": {\"flow_token\": \"AQBBBBBCS5FpgQ_cAAAAAD0QI3s.\", \"firstName\": \"Mahesh \", \"lastName\": \"Sattala \", \"pincode\": \"523456\", \"district\": \"Bangalore \", \"houseNumber\": \"5-6\", \"dateOfBirth\": \"1703257240046\", \"source\": \"SOCIAL_MEDIA\", \"landmark\": \"HSR Layout \", \"email\": \"maheshsattala@gmail.com\"}}' assert whatsapp_msg_handler.call_args[0][2] == '919515991111' metadata = whatsapp_msg_handler.call_args[0][3] metadata.pop("timestamp") - print(metadata) assert metadata == { 'context': {'from': '918657011111', 'id': 'wamid.HBgMOTE5NTE1OTkxNjg1FQIAERgSMjVGRjYwODI3RkMyOEQ0NUM1AA=='}, 'from': '919515991111', 'id': 'wamid.HBgMOTE5NTE1OTkxNjg1FQIAEhggQTRBQUYyODNBQkMwNEIzRDQ0MUI1ODkyMTE2NTMA', From 397b6248914543a2b963f7c0dd59bd8ae23aea15 Mon Sep 17 00:00:00 2001 From: Mahesh Date: Mon, 8 Jan 2024 09:24:07 +0530 Subject: [PATCH 3/5] Added review changes. --- kairon/chat/handlers/channels/whatsapp.py | 9 +++++---- tests/integration_test/chat_service_test.py | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/kairon/chat/handlers/channels/whatsapp.py b/kairon/chat/handlers/channels/whatsapp.py index 2e83b7612..de851eac0 100644 --- a/kairon/chat/handlers/channels/whatsapp.py +++ b/kairon/chat/handlers/channels/whatsapp.py @@ -41,10 +41,11 @@ async def message( if message.get("type") == "interactive": interactive_type = message.get("interactive").get("type") if interactive_type == "nfm_reply": - response_json = json.dumps( - {interactive_type: json.loads(message["interactive"][interactive_type]['response_json'])} - ) - text = f"/k_flow_msg{response_json}" + logger.debug(message["interactive"][interactive_type]) + response_json = json.loads(message["interactive"][interactive_type]['response_json']) + response_json.update({"type": interactive_type}) + entity = json.dumps({interactive_type: response_json}) + text = f"/k_interactive_msg{entity}" else: text = message["interactive"][interactive_type]["id"] elif message.get("type") == "text": diff --git a/tests/integration_test/chat_service_test.py b/tests/integration_test/chat_service_test.py index 2eda59e86..5ac2bb47c 100644 --- a/tests/integration_test/chat_service_test.py +++ b/tests/integration_test/chat_service_test.py @@ -1649,7 +1649,7 @@ def _mock_validate_hub_signature(*args, **kwargs): actual = response.json() assert actual == 'success' assert len(whatsapp_msg_handler.call_args[0]) == 5 - assert whatsapp_msg_handler.call_args[0][1] == '/k_flow_msg{\"nfm_reply\": {\"flow_token\": \"AQBBBBBCS5FpgQ_cAAAAAD0QI3s.\", \"firstName\": \"Mahesh \", \"lastName\": \"Sattala \", \"pincode\": \"523456\", \"district\": \"Bangalore \", \"houseNumber\": \"5-6\", \"dateOfBirth\": \"1703257240046\", \"source\": \"SOCIAL_MEDIA\", \"landmark\": \"HSR Layout \", \"email\": \"maheshsattala@gmail.com\"}}' + assert whatsapp_msg_handler.call_args[0][1] == '/k_interactive_msg{\"nfm_reply\": {\"flow_token\": \"AQBBBBBCS5FpgQ_cAAAAAD0QI3s.\", \"firstName\": \"Mahesh \", \"lastName\": \"Sattala \", \"pincode\": \"523456\", \"district\": \"Bangalore \", \"houseNumber\": \"5-6\", \"dateOfBirth\": \"1703257240046\", \"source\": \"SOCIAL_MEDIA\", \"landmark\": \"HSR Layout \", \"email\": \"maheshsattala@gmail.com\", \"type\": \"nfm_reply\"}}' assert whatsapp_msg_handler.call_args[0][2] == '919515991111' metadata = whatsapp_msg_handler.call_args[0][3] metadata.pop("timestamp") From 2eda8aa811f5df399022874e5c0dd0a2196b46d5 Mon Sep 17 00:00:00 2001 From: Mahesh Date: Mon, 8 Jan 2024 14:20:36 +0530 Subject: [PATCH 4/5] Made review changes. --- kairon/chat/handlers/channels/whatsapp.py | 2 +- kairon/shared/constants.py | 1 + tests/integration_test/chat_service_test.py | 2 +- tests/integration_test/services_test.py | 7 ++++--- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/kairon/chat/handlers/channels/whatsapp.py b/kairon/chat/handlers/channels/whatsapp.py index de851eac0..899d4fcdf 100644 --- a/kairon/chat/handlers/channels/whatsapp.py +++ b/kairon/chat/handlers/channels/whatsapp.py @@ -44,7 +44,7 @@ async def message( logger.debug(message["interactive"][interactive_type]) response_json = json.loads(message["interactive"][interactive_type]['response_json']) response_json.update({"type": interactive_type}) - entity = json.dumps({interactive_type: response_json}) + entity = json.dumps({"flow_reply": response_json}) text = f"/k_interactive_msg{entity}" else: text = message["interactive"][interactive_type]["id"] diff --git a/kairon/shared/constants.py b/kairon/shared/constants.py index 8d00869ac..5f18109dd 100644 --- a/kairon/shared/constants.py +++ b/kairon/shared/constants.py @@ -142,6 +142,7 @@ class KaironSystemSlots(str, Enum): document = "document" doc_url = "doc_url" order = "order" + flow_reply = "flow_reply" class VectorEmbeddingsDatabases(str, Enum): diff --git a/tests/integration_test/chat_service_test.py b/tests/integration_test/chat_service_test.py index 5ac2bb47c..a4ad18f4a 100644 --- a/tests/integration_test/chat_service_test.py +++ b/tests/integration_test/chat_service_test.py @@ -1649,7 +1649,7 @@ def _mock_validate_hub_signature(*args, **kwargs): actual = response.json() assert actual == 'success' assert len(whatsapp_msg_handler.call_args[0]) == 5 - assert whatsapp_msg_handler.call_args[0][1] == '/k_interactive_msg{\"nfm_reply\": {\"flow_token\": \"AQBBBBBCS5FpgQ_cAAAAAD0QI3s.\", \"firstName\": \"Mahesh \", \"lastName\": \"Sattala \", \"pincode\": \"523456\", \"district\": \"Bangalore \", \"houseNumber\": \"5-6\", \"dateOfBirth\": \"1703257240046\", \"source\": \"SOCIAL_MEDIA\", \"landmark\": \"HSR Layout \", \"email\": \"maheshsattala@gmail.com\", \"type\": \"nfm_reply\"}}' + assert whatsapp_msg_handler.call_args[0][1] == '/k_interactive_msg{\"flow_reply\": {\"flow_token\": \"AQBBBBBCS5FpgQ_cAAAAAD0QI3s.\", \"firstName\": \"Mahesh \", \"lastName\": \"Sattala \", \"pincode\": \"523456\", \"district\": \"Bangalore \", \"houseNumber\": \"5-6\", \"dateOfBirth\": \"1703257240046\", \"source\": \"SOCIAL_MEDIA\", \"landmark\": \"HSR Layout \", \"email\": \"maheshsattala@gmail.com\", \"type\": \"nfm_reply\"}}' assert whatsapp_msg_handler.call_args[0][2] == '919515991111' metadata = whatsapp_msg_handler.call_args[0][3] metadata.pop("timestamp") diff --git a/tests/integration_test/services_test.py b/tests/integration_test/services_test.py index c719ea583..cf4885af5 100644 --- a/tests/integration_test/services_test.py +++ b/tests/integration_test/services_test.py @@ -2911,7 +2911,7 @@ def test_list_entities_empty(): ) actual = response.json() assert actual["error_code"] == 0 - assert len(actual['data']) == 8 + assert len(actual['data']) == 9 assert actual["success"] @@ -3036,7 +3036,8 @@ def test_list_entities(): assert actual["error_code"] == 0 assert {e['name'] for e in actual["data"]} == {'bot', 'file', 'category', 'file_text', 'ticketid', 'file_error', 'priority', 'requested_slot', 'fdresponse', 'kairon_action_response', - 'audio', 'image', 'doc_url', 'document', 'video', 'order'} + 'audio', 'image', 'doc_url', 'document', 'video', 'order', + 'flow_reply'} assert actual["success"] @@ -3437,7 +3438,7 @@ def test_get_slots(): ) actual = response.json() assert "data" in actual - assert len(actual["data"]) == 15 + assert len(actual["data"]) == 16 assert actual["success"] assert actual["error_code"] == 0 assert Utility.check_empty_string(actual["message"]) From 39534b3bbba3542006b1c4144578316a999459d3 Mon Sep 17 00:00:00 2001 From: Mahesh Date: Mon, 8 Jan 2024 16:56:37 +0530 Subject: [PATCH 5/5] Made review changes and fixed test cases. --- .../data_processor/data_processor_test.py | 82 ++++++++++--------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/tests/unit_test/data_processor/data_processor_test.py b/tests/unit_test/data_processor/data_processor_test.py index bb99361bd..f9c4a0ab7 100644 --- a/tests/unit_test/data_processor/data_processor_test.py +++ b/tests/unit_test/data_processor/data_processor_test.py @@ -1035,7 +1035,7 @@ async def test_save_from_path_yml(self): assert len(list(Intents.objects(bot="test_load_yml", user="testUser", use_entities=False))) == 5 assert len(list(Intents.objects(bot="test_load_yml", user="testUser", use_entities=True))) == 27 assert len( - list(Slots.objects(bot="test_load_yml", user="testUser", influence_conversation=True, status=True))) == 7 + list(Slots.objects(bot="test_load_yml", user="testUser", influence_conversation=True, status=True))) == 8 assert len( list(Slots.objects(bot="test_load_yml", user="testUser", influence_conversation=False, status=True))) == 9 multiflow_stories = processor.load_multiflow_stories_yaml(bot='test_load_yml') @@ -1455,11 +1455,11 @@ async def test_upload_case_insensitivity(self): domain = processor.load_domain("test_upload_case_insensitivity") assert all(slot.name in ['session_started_metadata', 'requested_slot', 'application_name', 'bot', 'email_id', 'location', 'user', 'kairon_action_response', 'image', 'video', 'audio', 'doc_url', - 'document', 'order'] for slot in domain.slots) + 'document', 'order', 'flow_reply'] for slot in domain.slots) assert list(domain.templates.keys()) == ['utter_please_rephrase', 'utter_greet', 'utter_goodbye', 'utter_default'] assert domain.entities == ['user', 'location', 'email_id', 'application_name', 'bot', 'kairon_action_response', - 'order', 'image', 'audio', 'video', 'document', 'doc_url'] + 'order', 'image', 'audio', 'video', 'document', 'doc_url', 'flow_reply'] assert domain.forms == {'ask_user': {'required_slots': {'user': [{'type': 'from_entity', 'entity': 'user'}], 'email_id': [ {'type': 'from_entity', 'entity': 'email_id'}]}}, @@ -1563,8 +1563,8 @@ async def test_load_from_path_yml_training_files(self): assert story_graph.story_steps[15].events[2].entities[0]['entity'] == 'fdresponse' domain = processor.load_domain("test_load_from_path_yml_training_files") assert isinstance(domain, Domain) - assert domain.slots.__len__() == 18 - assert len([slot for slot in domain.slots if slot.influence_conversation is True]) == 7 + assert domain.slots.__len__() == 19 + assert len([slot for slot in domain.slots if slot.influence_conversation is True]) == 8 assert len([slot for slot in domain.slots if slot.influence_conversation is False]) == 11 assert domain.intent_properties.__len__() == 32 assert len([intent for intent in domain.intent_properties.keys() if @@ -1572,7 +1572,7 @@ async def test_load_from_path_yml_training_files(self): assert len([intent for intent in domain.intent_properties.keys() if not domain.intent_properties.get(intent)['used_entities']]) == 5 assert domain.templates.keys().__len__() == 29 - assert domain.entities.__len__() == 17 + assert domain.entities.__len__() == 18 assert domain.forms.__len__() == 2 assert domain.forms.__len__() == 2 assert domain.forms['ticket_attributes_form'] == { @@ -1636,9 +1636,9 @@ async def test_load_from_path_all_scenario(self): assert story_graph.story_steps[15].events[2].entities[0]['entity'] == 'fdresponse' domain = processor.load_domain("all") assert isinstance(domain, Domain) - assert domain.slots.__len__() == 17 + assert domain.slots.__len__() == 18 assert domain.templates.keys().__len__() == 27 - assert domain.entities.__len__() == 16 + assert domain.entities.__len__() == 17 assert domain.forms.__len__() == 2 assert domain.forms['ticket_attributes_form'] == {'required_slots': {}} assert isinstance(domain.forms, dict) @@ -1682,9 +1682,9 @@ async def test_load_from_path_all_scenario_append(self): assert story_graph.story_steps[15].events[2].entities[0]['entity'] == 'fdresponse' domain = processor.load_domain("all") assert isinstance(domain, Domain) - assert domain.slots.__len__() == 17 + assert domain.slots.__len__() == 18 assert domain.templates.keys().__len__() == 27 - assert domain.entities.__len__() == 16 + assert domain.entities.__len__() == 17 assert domain.forms.__len__() == 2 assert isinstance(domain.forms, dict) assert domain.user_actions.__len__() == 40 @@ -1710,10 +1710,10 @@ def test_load_domain(self): processor = MongoProcessor() domain = processor.load_domain("tests") assert isinstance(domain, Domain) - assert domain.slots.__len__() == 9 + assert domain.slots.__len__() == 10 assert [s.name for s in domain.slots if s.name == 'kairon_action_response' and s.value is None] assert domain.templates.keys().__len__() == 11 - assert domain.entities.__len__() == 8 + assert domain.entities.__len__() == 9 assert domain.form_names.__len__() == 0 assert domain.user_actions.__len__() == 11 assert domain.intents.__len__() == 14 @@ -1960,7 +1960,7 @@ def test_add_training_example_with_entity(self): ) slots = Slots.objects(bot="tests") new_slot = slots.get(name="priority") - assert slots.__len__() == 9 + assert slots.__len__() == 10 assert new_slot.name == "priority" assert new_slot.type == "text" assert new_training_example.text == "Log a critical issue" @@ -1993,7 +1993,7 @@ def test_get_training_examples_with_entities(self): for value in actual ] ) - assert slots.__len__() == 10 + assert slots.__len__() == 11 assert new_slot.name == "ticketid" assert new_slot.type == "text" expected = ["hey", "hello", "hi", "good morning", "good evening", "hey there"] @@ -2036,7 +2036,7 @@ def test_add_entity(self): def test_get_entities(self): processor = MongoProcessor() expected = ["bot", "priority", "file_text", "ticketid", 'kairon_action_response', 'image', 'video', 'audio', - 'doc_url', 'document', 'order'] + 'doc_url', 'document', 'order', 'flow_reply'] actual = processor.get_entities("tests") assert actual.__len__() == expected.__len__() assert all(item["name"] in expected for item in actual) @@ -4690,8 +4690,8 @@ def _mock_bot_info(*args, **kwargs): assert story_graph.story_steps[15].events[2].entities[0]['entity'] == 'fdresponse' domain = mongo_processor.load_domain(bot) assert isinstance(domain, Domain) - assert domain.slots.__len__() == 18 - assert len([slot for slot in domain.slots if slot.influence_conversation is True]) == 7 + assert domain.slots.__len__() == 19 + assert len([slot for slot in domain.slots if slot.influence_conversation is True]) == 8 assert len([slot for slot in domain.slots if slot.influence_conversation is False]) == 11 assert domain.intent_properties.__len__() == 32 assert len([intent for intent in domain.intent_properties.keys() if @@ -4699,7 +4699,7 @@ def _mock_bot_info(*args, **kwargs): assert len([intent for intent in domain.intent_properties.keys() if not domain.intent_properties.get(intent)['used_entities']]) == 5 assert domain.templates.keys().__len__() == 29 - assert domain.entities.__len__() == 17 + assert domain.entities.__len__() == 18 assert domain.form_names.__len__() == 2 assert domain.user_actions.__len__() == 48 assert domain.intents.__len__() == 32 @@ -4756,9 +4756,9 @@ def _mock_bot_info(*args, **kwargs): assert story_graph.story_steps[15].events[2].entities[0]['entity'] == 'fdresponse' domain = mongo_processor.load_domain(bot) assert isinstance(domain, Domain) - assert domain.slots.__len__() == 17 + assert domain.slots.__len__() == 18 assert domain.templates.keys().__len__() == 27 - assert domain.entities.__len__() == 16 + assert domain.entities.__len__() == 17 assert domain.form_names.__len__() == 2 assert domain.user_actions.__len__() == 40 assert domain.intents.__len__() == 29 @@ -4811,8 +4811,8 @@ def _mock_bot_info(*args, **kwargs): assert story_graph.story_steps[15].events[2].entities[0]['entity'] == 'fdresponse' domain = mongo_processor.load_domain(bot) assert isinstance(domain, Domain) - assert domain.slots.__len__() == 18 - assert len([slot for slot in domain.slots if slot.influence_conversation is True]) == 7 + assert domain.slots.__len__() == 19 + assert len([slot for slot in domain.slots if slot.influence_conversation is True]) == 8 assert len([slot for slot in domain.slots if slot.influence_conversation is False]) == 11 assert domain.intent_properties.__len__() == 32 assert len([intent for intent in domain.intent_properties.keys() if @@ -4820,7 +4820,7 @@ def _mock_bot_info(*args, **kwargs): assert len([intent for intent in domain.intent_properties.keys() if not domain.intent_properties.get(intent)['used_entities']]) == 5 assert domain.templates.keys().__len__() == 29 - assert domain.entities.__len__() == 17 + assert domain.entities.__len__() == 18 assert domain.form_names.__len__() == 2 assert domain.user_actions.__len__() == 48 assert domain.intents.__len__() == 32 @@ -4874,8 +4874,8 @@ def _mock_bot_info(*args, **kwargs): assert story_graph.story_steps[15].events[2].entities[0]['entity'] == 'fdresponse' domain = mongo_processor.load_domain(bot) assert isinstance(domain, Domain) - assert domain.slots.__len__() == 18 - assert len([slot for slot in domain.slots if slot.influence_conversation is True]) == 7 + assert domain.slots.__len__() == 19 + assert len([slot for slot in domain.slots if slot.influence_conversation is True]) == 8 assert len([slot for slot in domain.slots if slot.influence_conversation is False]) == 11 assert domain.intent_properties.__len__() == 33 assert len([intent for intent in domain.intent_properties.keys() if @@ -4883,7 +4883,7 @@ def _mock_bot_info(*args, **kwargs): assert len([intent for intent in domain.intent_properties.keys() if not domain.intent_properties.get(intent)['used_entities']]) == 6 assert domain.templates.keys().__len__() == 31 - assert domain.entities.__len__() == 17 + assert domain.entities.__len__() == 18 assert domain.form_names.__len__() == 2 assert domain.user_actions.__len__() == 50 assert domain.intents.__len__() == 33 @@ -4924,8 +4924,8 @@ def test_delete_nlu_only(self): assert story_graph.story_steps[15].events[2].entities[0]['entity'] == 'fdresponse' domain = mongo_processor.load_domain(bot) assert isinstance(domain, Domain) - assert domain.slots.__len__() == 18 - assert len([slot for slot in domain.slots if slot.influence_conversation is True]) == 7 + assert domain.slots.__len__() == 19 + assert len([slot for slot in domain.slots if slot.influence_conversation is True]) == 8 assert len([slot for slot in domain.slots if slot.influence_conversation is False]) == 11 assert domain.intent_properties.__len__() == 33 assert len([intent for intent in domain.intent_properties.keys() if @@ -4933,7 +4933,7 @@ def test_delete_nlu_only(self): assert len([intent for intent in domain.intent_properties.keys() if not domain.intent_properties.get(intent)['used_entities']]) == 6 assert domain.templates.keys().__len__() == 31 - assert domain.entities.__len__() == 17 + assert domain.entities.__len__() == 18 assert domain.form_names.__len__() == 2 assert domain.user_actions.__len__() == 50 assert domain.intents.__len__() == 33 @@ -4981,8 +4981,8 @@ def test_delete_stories_only(self): assert story_graph.story_steps.__len__() == 0 domain = mongo_processor.load_domain(bot) assert isinstance(domain, Domain) - assert domain.slots.__len__() == 18 - assert len([slot for slot in domain.slots if slot.influence_conversation is True]) == 7 + assert domain.slots.__len__() == 19 + assert len([slot for slot in domain.slots if slot.influence_conversation is True]) == 8 assert len([slot for slot in domain.slots if slot.influence_conversation is False]) == 11 assert domain.intent_properties.__len__() == 33 assert len([intent for intent in domain.intent_properties.keys() if @@ -4990,7 +4990,7 @@ def test_delete_stories_only(self): assert len([intent for intent in domain.intent_properties.keys() if not domain.intent_properties.get(intent)['used_entities']]) == 6 assert domain.templates.keys().__len__() == 31 - assert domain.entities.__len__() == 17 + assert domain.entities.__len__() == 18 assert domain.form_names.__len__() == 2 assert domain.user_actions.__len__() == 50 assert domain.intents.__len__() == 33 @@ -5026,8 +5026,8 @@ def test_delete_multiflow_stories_only(self): assert story_graph.story_steps.__len__() == 0 domain = mongo_processor.load_domain(bot) assert isinstance(domain, Domain) - assert domain.slots.__len__() == 18 - assert len([slot for slot in domain.slots if slot.influence_conversation is True]) == 7 + assert domain.slots.__len__() == 19 + assert len([slot for slot in domain.slots if slot.influence_conversation is True]) == 8 assert len([slot for slot in domain.slots if slot.influence_conversation is False]) == 11 assert domain.intent_properties.__len__() == 33 assert len([intent for intent in domain.intent_properties.keys() if @@ -5035,7 +5035,7 @@ def test_delete_multiflow_stories_only(self): assert len([intent for intent in domain.intent_properties.keys() if not domain.intent_properties.get(intent)['used_entities']]) == 6 assert domain.templates.keys().__len__() == 31 - assert domain.entities.__len__() == 17 + assert domain.entities.__len__() == 18 assert domain.form_names.__len__() == 2 assert domain.user_actions.__len__() == 50 assert domain.intents.__len__() == 33 @@ -5080,10 +5080,10 @@ def test_delete_config_and_actions_only(self): assert story_graph.story_steps.__len__() == 16 domain = mongo_processor.load_domain(bot) assert isinstance(domain, Domain) - assert domain.slots.__len__() == 18 + assert domain.slots.__len__() == 19 assert domain.intent_properties.__len__() == 33 assert domain.templates.keys().__len__() == 31 - assert domain.entities.__len__() == 17 + assert domain.entities.__len__() == 18 assert domain.form_names.__len__() == 2 assert domain.user_actions.__len__() == 43 assert domain.intents.__len__() == 33 @@ -5156,10 +5156,10 @@ async def test_save_rules_and_domain_only(self, get_training_data): assert len(rules) == 3 domain = mongo_processor.load_domain(bot) assert isinstance(domain, Domain) - assert domain.slots.__len__() == 18 + assert domain.slots.__len__() == 19 assert domain.intent_properties.__len__() == 32 assert domain.templates.keys().__len__() == 27 - assert domain.entities.__len__() == 17 + assert domain.entities.__len__() == 18 assert domain.form_names.__len__() == 2 assert domain.user_actions.__len__() == 46 assert domain.intents.__len__() == 32 @@ -6841,7 +6841,7 @@ def test_get_slot(self): bot = 'test' processor = MongoProcessor() slots = list(processor.get_existing_slots(bot)) - assert len(slots) == 18 + assert len(slots) == 19 assert slots == [ {'name': 'bot', 'type': 'any', 'initial_value': 'test', 'auto_fill': False, 'influence_conversation': False, '_has_been_set': False}, @@ -6859,6 +6859,8 @@ def test_get_slot(self): '_has_been_set': False}, {'name': 'doc_url', 'type': 'text', 'auto_fill': True, 'influence_conversation': True, '_has_been_set': False}, + {'name': 'flow_reply', 'type': 'text', 'auto_fill': True, 'influence_conversation': True, + '_has_been_set': False}, {'name': 'category', 'type': 'unfeaturized', 'auto_fill': True, 'influence_conversation': False, '_has_been_set': False}, {'name': 'file', 'type': 'unfeaturized', 'auto_fill': True, 'influence_conversation': False,