def test_nlg_fill_response_custom(slot_name: Text, slot_value: Any): response = { "custom": { "field": f"{{{slot_name}}}", "properties": { "field_prefixed": f"prefix_{{{slot_name}}}" }, "bool_field": True, "int_field:": 42, "empty_field": None, } } t = TemplatedNaturalLanguageGenerator(responses=dict()) result = t._fill_response(response=response, filled_slots={slot_name: slot_value}) assert result == { "custom": { "field": str(slot_value), "properties": { "field_prefixed": f"prefix_{slot_value}" }, "bool_field": True, "int_field:": 42, "empty_field": None, } }
def test_nlg_fill_response_text_and_custom(text_slot_name, text_slot_value, cust_slot_name, cust_slot_value): response = { "text": f"{{{text_slot_name}}}", "custom": { "field": f"{{{cust_slot_name}}}", "properties": { "field_prefixed": f"prefix_{{{cust_slot_name}}}" }, }, } t = TemplatedNaturalLanguageGenerator(responses=dict()) result = t._fill_response( response=response, filled_slots={ text_slot_name: text_slot_value, cust_slot_name: cust_slot_value }, ) assert result == { "text": str(text_slot_value), "custom": { "field": str(cust_slot_value), "properties": { "field_prefixed": f"prefix_{str(cust_slot_value)}" }, }, }
def test_nlg_fill_response_quick_replies(quick_replies_slot_name, quick_replies_slot_value): response = {"quick_replies": f"{{{quick_replies_slot_name}}}"} t = TemplatedNaturalLanguageGenerator(responses=dict()) result = t._fill_response( response=response, filled_slots={quick_replies_slot_name: quick_replies_slot_value}, ) assert result == {"quick_replies": str(quick_replies_slot_value)}
def test_nlg_fill_response_text_with_json(response_text, expected): response = {"text": response_text} t = TemplatedNaturalLanguageGenerator(responses=dict()) result = t._fill_response(response=response, filled_slots={ "slot_1": "foo", "slot_2": "bar" }) assert result == {"text": expected}
async def test_run_end_to_end_utterance_action(): end_to_end_utterance = "Hi" domain = Domain.from_yaml( textwrap.dedent(f""" actions: - my_action {KEY_E2E_ACTIONS}: - {end_to_end_utterance} - Bye Bye """)) e2e_action = action.action_for_name_or_text("Hi", domain, None) events = await e2e_action.run( CollectingOutputChannel(), TemplatedNaturalLanguageGenerator(domain.responses), DialogueStateTracker.from_events("sender", evts=[]), domain, ) assert events == [ BotUttered( end_to_end_utterance, { "elements": None, "quick_replies": None, "buttons": None, "attachment": None, "image": None, "custom": None, }, {}, ) ]
async def test_nlg_conditional_response_variations_with_yaml_and_channel(): domain = Domain.from_file( path="data/test_domains/conditional_response_variations.yml" ) t = TemplatedNaturalLanguageGenerator(responses=domain.responses) slot = CategoricalSlot( name="account_type", mappings=[{}], initial_value="primary", influence_conversation=False, ) tracker = DialogueStateTracker(sender_id="conversation_id", slots=[slot]) r = await t.generate( utter_action="utter_check_balance", tracker=tracker, output_channel="os" ) assert ( r.get("text") == "As a primary account holder, you can now set-up " "your access on mobile app too." ) resp = await t.generate( utter_action="utter_check_balance", tracker=tracker, output_channel="app" ) assert resp.get("text") == "Welcome to your app account overview."
async def test_nlg_conditional_response_variations_with_no_slots(): responses = { "utter_test": [ { "text": "Conditional OS Response A", "condition": [{"type": "slot", "name": "slot test", "value": "A"}], "channel": "os", }, { "text": "Conditional Response A", "condition": [{"type": "slot", "name": "slot test", "value": "A"}], }, { "text": "Conditional Response B", "condition": [{"type": "slot", "name": "slot test", "value": "B"}], }, {"text": "Default response"}, ] } t = TemplatedNaturalLanguageGenerator(responses=responses) no_slots_tracker = DialogueStateTracker(sender_id="nlg_test_default", slots=None) default_response = await t.generate( utter_action="utter_test", tracker=no_slots_tracker, output_channel="" ) assert default_response.get("text") == "Default response"
async def test_nlg_when_multiple_conditions_satisfied(): responses = { "utter_action": [ { "text": "example A", "condition": [{"type": "slot", "name": "test", "value": "A"}], }, { "text": "example B", "condition": [{"type": "slot", "name": "test_another", "value": "B"}], }, { "text": "non matching example 1", "condition": [ {"type": "slot", "name": "test_third_slot", "value": "C"} ], }, { "text": "non matching example 2", "condition": [{"type": "slot", "name": "test", "value": "D"}], }, ] } t = TemplatedNaturalLanguageGenerator(responses=responses) slot_a = TextSlot(name="test", initial_value="A", influence_conversation=False) slot_b = TextSlot( name="test_another", initial_value="B", influence_conversation=False ) tracker = DialogueStateTracker(sender_id="test_nlg", slots=[slot_a, slot_b]) resp = await t.generate( utter_action="utter_action", tracker=tracker, output_channel="" ) assert resp.get("text") in ["example A", "example B"]
async def test_nlg_conditional_response_variations_with_diff_slot_types( slot_name: Text, slot_value: Any, message: Text ): responses = { "utter_action": [ { "text": "example boolean", "condition": [{"type": "slot", "name": "test_bool", "value": True}], }, { "text": "example integer", "condition": [{"type": "slot", "name": "test_int", "value": 12}], }, { "text": "example list", "condition": [{"type": "slot", "name": "test_list", "value": []}], }, ] } t = TemplatedNaturalLanguageGenerator(responses=responses) slot = AnySlot( name=slot_name, initial_value=slot_value, influence_conversation=False ) tracker = DialogueStateTracker(sender_id="nlg_tracker", slots=[slot]) r = await t.generate( utter_action="utter_action", tracker=tracker, output_channel="" ) assert r.get("text") == message
async def test_nlg_conditional_response_variations_with_interpolated_slots( slot_name: Text, slot_value: Any, response_variation: Text): responses = { "utter_action": [ { "text": "example one {test}", "condition": [{ "type": "slot", "name": "test", "value": "A" }], }, { "text": "example two {test}", "condition": [{ "type": "slot", "name": "test", "value": "B" }], }, ] } t = TemplatedNaturalLanguageGenerator(responses=responses) slot = TextSlot( name=slot_name, mappings=[{}], initial_value=slot_value, influence_conversation=False, ) tracker = DialogueStateTracker(sender_id="nlg_interpolated", slots=[slot]) r = await t.generate(utter_action="utter_action", tracker=tracker, output_channel="") assert r.get("text") == response_variation
def template_nlg() -> TemplatedNaturalLanguageGenerator: responses = { "utter_ask_rephrase": [{ "text": "can you rephrase that?" }], "utter_restart": [{ "text": "congrats, you've restarted me!" }], "utter_back": [{ "text": "backing up..." }], "utter_invalid": [{ "text": "a response referencing an invalid {variable}." }], "utter_buttons": [{ "text": "button message", "buttons": [ { "payload": "button1", "title": "button1" }, { "payload": "button2", "title": "button2" }, ], }], } return TemplatedNaturalLanguageGenerator(responses)
async def test_nlg_conditional_response_variations_with_yaml_multi_constraints(): domain = Domain.from_file( path="data/test_domains/conditional_response_variations.yml" ) t = TemplatedNaturalLanguageGenerator(responses=domain.responses) first_slot = CategoricalSlot( name="account_type", mappings=[{}], initial_value="primary", influence_conversation=False, ) second_slot = BooleanSlot( name="can_withdraw", mappings=[{}], initial_value=True, influence_conversation=False, ) tracker = DialogueStateTracker( sender_id="conversation_id", slots=[first_slot, second_slot] ) r = await t.generate( utter_action="utter_withdraw", tracker=tracker, output_channel="" ) assert r.get("text") == "Withdrawal has been approved."
def test_nlg_fill_response_button(button_slot_name, button_slot_value): response = { "buttons": [{ "payload": f'/choose{{{{"some_slot": "{{{button_slot_name}}}"}}}}', "title": f"{{{button_slot_name}}}", }] } t = TemplatedNaturalLanguageGenerator(responses=dict()) result = t._fill_response( response=response, filled_slots={button_slot_name: button_slot_value}) assert result == { "buttons": [{ "payload": f'/choose{{"some_slot": "{button_slot_value}"}}', "title": f"{button_slot_value}", }] }
def test_nlg_fill_response_image_and_text(text_slot_name, text_slot_value, img_slot_name, img_slot_value): response = { "text": f"{{{text_slot_name}}}", "image": f"{{{img_slot_name}}}" } t = TemplatedNaturalLanguageGenerator(responses=dict()) result = t._fill_response( response=response, filled_slots={ text_slot_name: text_slot_value, img_slot_name: img_slot_value }, ) assert result == { "text": str(text_slot_value), "image": str(img_slot_value) }
async def test_nlg_non_matching_channel(): domain = Domain.from_yaml(""" version: "{LATEST_TRAINING_DATA_FORMAT_VERSION}" responses: utter_hi: - text: "Hello" - text: "Hello Slack" channel: "slack" """) t = TemplatedNaturalLanguageGenerator(domain.responses) tracker = DialogueStateTracker(sender_id="test", slots=[]) r = await t.generate("utter_hi", tracker, "signal") assert r.get("text") == "Hello"
def test_nlg_fill_response_custom_with_list(): response = { "custom": { "blocks": [{ "fields": [{ "text": "*Departure date:*\n{test}" }] }], "other": ["{test}"], } } t = TemplatedNaturalLanguageGenerator(responses=dict()) result = t._fill_response(response=response, filled_slots={"test": 5}) assert result == { "custom": { "blocks": [{ "fields": [{ "text": "*Departure date:*\n5" }] }], "other": ["5"], } }
async def test_remote_action_with_template_param( default_channel: OutputChannel, default_tracker: DialogueStateTracker, domain: Domain, ): endpoint = EndpointConfig("https://example.com/webhooks/actions") remote_action = action.RemoteAction("my_action", endpoint) response = { "events": [ { "event": "form", "name": "restaurant_form", "timestamp": None }, { "event": "slot", "timestamp": None, "name": "requested_slot", "value": "cuisine", }, ], "responses": [{ "text": None, "buttons": [], "elements": [], "custom": {}, "template": "utter_ask_cuisine", "image": None, "attachment": None, }], } nlg = TemplatedNaturalLanguageGenerator( {"utter_ask_cuisine": [{ "text": "what dou want to eat?" }]}) with aioresponses() as mocked: mocked.post("https://example.com/webhooks/actions", payload=response) with pytest.warns(FutureWarning): events = await remote_action.run(default_channel, nlg, default_tracker, domain) assert events == [ BotUttered("what dou want to eat?", metadata={"utter_action": "utter_ask_cuisine"}), ActiveLoop("restaurant_form"), SlotSet("requested_slot", "cuisine"), ]
async def test_nlg_conditional_response_variations_with_yaml_single_condition( slot_name: Text, slot_value: Any, bot_message: Text): domain = Domain.from_file( path="data/test_domains/conditional_response_variations.yml") t = TemplatedNaturalLanguageGenerator(responses=domain.responses) slot = AnySlot(name=slot_name, initial_value=slot_value, influence_conversation=False) tracker = DialogueStateTracker(sender_id="conversation_id", slots=[slot]) r = await t.generate(utter_action="utter_withdraw", tracker=tracker, output_channel="") assert r.get("text") == bot_message
async def test_nlg_conditional_response_variations_channel_no_condition_met(): domain = Domain.from_yaml(f""" version: "{LATEST_TRAINING_DATA_FORMAT_VERSION}" responses: utter_action: - text: "example with channel" condition: - type: slot name: test value: A channel: os - text: "default" """) t = TemplatedNaturalLanguageGenerator(domain.responses) tracker = DialogueStateTracker(sender_id="test", slots=[]) r = await t.generate("utter_action", tracker, "os") assert r.get("text") == "default"
async def test_nlg_conditional_response_variations_with_slot_not_a_constraint(): domain = Domain.from_yaml( """ version: "2.0" responses: utter_action: - text: "text A" condition: - type: slot name: account value: "A" """ ) t = TemplatedNaturalLanguageGenerator(domain.responses) slot = TextSlot(name="account", initial_value="B", influence_conversation=False) tracker = DialogueStateTracker(sender_id="test", slots=[slot]) r = await t.generate("utter_action", tracker, "") assert r is None
async def test_nlg_conditional_response_variations_with_none_slot(): domain = Domain.from_yaml(f""" version: "{LATEST_TRAINING_DATA_FORMAT_VERSION}" responses: utter_action: - text: "text A" condition: - type: slot name: account value: "A" """) t = TemplatedNaturalLanguageGenerator(domain.responses) slot = AnySlot(name="account", mappings=[{}], initial_value=None, influence_conversation=False) tracker = DialogueStateTracker(sender_id="test", slots=[slot]) r = await t.generate("utter_action", tracker, "") assert r is None
async def test_nlg_conditional_response_variations_condition_logging( caplog: LogCaptureFixture, ): domain = Domain.from_yaml( """ version: "3.0" responses: utter_action: - text: "example" condition: - type: slot name: test_A value: A - type: slot name: test_B value: B - text: "default" """ ) t = TemplatedNaturalLanguageGenerator(domain.responses) slot_A = TextSlot( name="test_A", mappings=[{}], initial_value="A", influence_conversation=False ) slot_B = TextSlot( name="test_B", mappings=[{}], initial_value="B", influence_conversation=False ) tracker = DialogueStateTracker(sender_id="test", slots=[slot_A, slot_B]) with caplog.at_level(logging.DEBUG): await t.generate("utter_action", tracker=tracker, output_channel="") assert any( "Selecting response variation with conditions:" in message for message in caplog.messages ) assert any( "[condition 1] type: slot | name: test_A | value: A" in message for message in caplog.messages ) assert any( "[condition 2] type: slot | name: test_B | value: B" in message for message in caplog.messages )
async def test_nlg_conditional_response_variation_condition_met_channel_mismatch( ): domain = Domain.from_yaml(""" version: "2.0" responses: utter_action: - text: "example with channel" condition: - type: slot name: test value: A channel: os - text: "app default" channel: app """) t = TemplatedNaturalLanguageGenerator(domain.responses) slot = TextSlot("test", "A", influence_conversation=False) tracker = DialogueStateTracker(sender_id="test", slots=[slot]) r = await t.generate("utter_action", tracker, "app") assert r.get("text") == "app default"
async def test_nlg_conditional_edgecases(slots, channel, expected_response): domain = Domain.from_yaml( """ version: "2.0" responses: utter_action: - text: "condition example A with channel" condition: - type: slot name: test value: A channel: app - text: "condition example C with channel" condition: - type: slot name: test value: C channel: app - text: "condition example A no channel" condition: - type: slot name: test value: A - text: "condition example B no channel" condition: - type: slot name: test value: B - text: "default" """ ) t = TemplatedNaturalLanguageGenerator(domain.responses) tracker = DialogueStateTracker(sender_id="test", slots=slots) r = await t.generate("utter_action", tracker, channel) assert r.get("text") == expected_response
async def test_nlg_conditional_response_variations_with_null_slot(): domain = Domain.from_yaml( """ version: "2.0" responses: utter_action: - text: "text for null" condition: - type: slot name: account value: null """ ) t = TemplatedNaturalLanguageGenerator(domain.responses) slot = AnySlot(name="account", initial_value=None, influence_conversation=False) tracker = DialogueStateTracker(sender_id="test", slots=[slot]) r = await t.generate("utter_action", tracker, "") assert r.get("text") == "text for null" tracker_no_slots = DialogueStateTracker(sender_id="new_test", slots=[]) r = await t.generate("utter_action", tracker_no_slots, "") assert r.get("text") == "text for null"
def test_nlg_fill_response_text(slot_name: Text, slot_value: Any): response = {"text": f"{{{slot_name}}}"} t = TemplatedNaturalLanguageGenerator(responses=dict()) result = t._fill_response(response=response, filled_slots={slot_name: slot_value}) assert result == {"text": str(slot_value)}
def test_nlg_fill_response_image(img_slot_name: Text, img_slot_value: Text): response = {"image": f"{{{img_slot_name}}}"} t = TemplatedNaturalLanguageGenerator(responses=dict()) result = t._fill_response(response=response, filled_slots={img_slot_name: img_slot_value}) assert result == {"image": str(img_slot_value)}
def test_nlg_fill_response_with_bad_slot_name(slot_name, slot_value): response_text = f"{{{slot_name}}}" t = TemplatedNaturalLanguageGenerator(responses=dict()) result = t._fill_response(response={"text": response_text}, filled_slots={slot_name: slot_value}) assert result["text"] == response_text
def test_nlg_fill_response_attachment(attach_slot_name, attach_slot_value): response = {"attachment": "{" + attach_slot_name + "}"} t = TemplatedNaturalLanguageGenerator(responses=dict()) result = t._fill_response( response=response, filled_slots={attach_slot_name: attach_slot_value}) assert result == {"attachment": str(attach_slot_value)}