Files
wehub-resource-sync dc6079821b
Docs Tests / Check for file changes (push) Has been cancelled
Docs Tests / Test Documentation (push) Has been cancelled
Docs Tests / Documentation Linting Checks (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-policies) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (ubuntu-24.04, 3.10) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (ubuntu-24.04, 3.8) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (ubuntu-24.04, 3.9) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (windows-2022, 3.10) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (windows-2022, 3.8) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (windows-2022, 3.9) (push) Has been cancelled
Continuous Integration / Check for file changes (push) Has been cancelled
Continuous Integration / Wait for docs tests (push) Has been cancelled
Continuous Integration / Code Quality (push) Has been cancelled
Continuous Integration / Check for changelog (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Upload coverage reports to codeclimate (push) Has been cancelled
Continuous Integration / Run Non-Sequential Integration Tests (push) Has been cancelled
Continuous Integration / Run Broker Integration Tests (push) Has been cancelled
Continuous Integration / Run Sequential Integration Tests (push) Has been cancelled
Continuous Integration / Build Docker base images and setup environment (push) Has been cancelled
Continuous Integration / Build Docker (default) (push) Has been cancelled
Continuous Integration / Build Docker (full) (push) Has been cancelled
Continuous Integration / Build Docker (mitie-en) (push) Has been cancelled
Continuous Integration / Build Docker (spacy-de) (push) Has been cancelled
Continuous Integration / Build Docker (spacy-en) (push) Has been cancelled
Continuous Integration / Build Docker (spacy-it) (push) Has been cancelled
Continuous Integration / Deploy to PyPI (push) Has been cancelled
Continuous Integration / Notify Slack & Publish Release Notes (push) Has been cancelled
Publish Documentation / Evaluate release tag (push) Has been cancelled
Publish Documentation / Prebuild Docs (push) Has been cancelled
Publish Documentation / Preview Docs (push) Has been cancelled
Publish Documentation / Check for file changes (push) Has been cancelled
Publish Documentation / Publish Docs (push) Has been cancelled
Automatic PR Merger / mergepal (push) Has been cancelled
CI Github Actions / Run Tests (push) Has been cancelled
Semgrep / Semgrep Workflow Security Scan (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:24:47 +08:00

628 lines
20 KiB
Python

from typing import Dict, List, Text, Any
import logging
import pytest
from _pytest.logging import LogCaptureFixture
from rasa.core.nlg.response import TemplatedNaturalLanguageGenerator
from rasa.shared.constants import LATEST_TRAINING_DATA_FORMAT_VERSION
from rasa.shared.core.domain import Domain
from rasa.shared.core.slots import TextSlot, AnySlot, CategoricalSlot, BooleanSlot
from rasa.shared.core.trackers import DialogueStateTracker
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", mappings=[{}], initial_value="A", influence_conversation=False
)
slot_b = TextSlot(
name="test_another",
mappings=[{}],
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"]
@pytest.fixture(scope="session")
def test_slots() -> List[object]:
slot_a = CategoricalSlot(
name="test",
mappings=[{"type": "from_text", "value": ["cold", "hot"]}],
initial_value="Cold",
influence_conversation=False,
)
slot_b = BooleanSlot(
name="test2",
mappings=[{}],
initial_value=False,
influence_conversation=False,
)
slot_c = CategoricalSlot(
name="test3",
mappings=[{"type": "from_text", "value": ["cold", "hot"]}],
initial_value="hot",
influence_conversation=False,
)
return [slot_a, slot_b, slot_c]
@pytest.fixture(scope="session")
def test_responses() -> List[Dict[Text, List[Dict[Text, Any]]]]:
return [
{
"utter_action": [
{
"text": "example a",
"condition": [{"type": "slot", "name": "test", "value": "cold"}],
}
]
},
{
"utter_action_multiple_conditions": [
{
"text": "example b",
"condition": [
{"type": "slot", "name": "test", "value": "cold"},
{"type": "slot", "name": "test2", "value": False},
{"type": "slot", "name": "test3", "value": "hot"},
],
}
]
},
]
async def test_nlg_slot_case_sensitivity(
test_slots: List[object],
test_responses: List[Dict[Text, List[Dict[Text, Any]]]],
):
utter_action = "utter_action"
t = TemplatedNaturalLanguageGenerator(responses=test_responses[0])
tracker = DialogueStateTracker(sender_id="test_nlg", slots=[test_slots[0]])
resp = await t.generate(
utter_action=utter_action, tracker=tracker, output_channel=""
)
assert resp.get("text") == test_responses[0][utter_action][0]["text"]
async def test_matches_filled_slots_multiple_conditions(
test_slots: List[object],
test_responses: List[Dict[Text, List[Dict[Text, Any]]]],
):
utter_action = "utter_action_multiple_conditions"
t = TemplatedNaturalLanguageGenerator(responses=test_responses[1])
tracker = DialogueStateTracker(sender_id="test_nlg", slots=test_slots)
resp = await t.generate(
utter_action=utter_action,
tracker=tracker,
output_channel="",
)
assert resp.get("text") == test_responses[1][utter_action][0]["text"]
async def test_matches_filled_slots_multiple_conditions_neg_match_boolean_slot(
test_slots: List[object], test_responses: List[Dict[Text, List[Dict[Text, Any]]]]
):
t = TemplatedNaturalLanguageGenerator(responses=test_responses[1])
test_slots[1].initial_value = True
tracker = DialogueStateTracker(sender_id="test_nlg", slots=test_slots)
resp = await t.generate(
utter_action="utter_action_multiple_conditions",
tracker=tracker,
output_channel="",
)
assert resp is None
async def test_matches_filled_slots_multiple_conditions_neg_match_in_first_slot(
test_slots: List[object], test_responses: List[Dict[Text, List[Dict[Text, Any]]]]
):
t = TemplatedNaturalLanguageGenerator(responses=test_responses[1])
test_slots[0].initial_value = "junk"
tracker = DialogueStateTracker(sender_id="test_nlg", slots=test_slots)
resp = await t.generate(
utter_action="utter_action_multiple_conditions",
tracker=tracker,
output_channel="",
)
assert resp is None
async def test_matches_filled_slots_multiple_conditions_neg_match_in_last_slot(
test_slots: List[object], test_responses: List[Dict[Text, List[Dict[Text, Any]]]]
):
t = TemplatedNaturalLanguageGenerator(responses=test_responses[1])
test_slots[2].initial_value = "junk"
tracker = DialogueStateTracker(sender_id="test_nlg", slots=test_slots)
resp = await t.generate(
utter_action="utter_action_multiple_conditions",
tracker=tracker,
output_channel="",
)
assert resp is None
@pytest.mark.parametrize(
("slot_name", "slot_value", "response_variation"),
(("test", "A", "example one A"), ("test", "B", "example two B")),
)
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
@pytest.mark.parametrize(
("slot_name", "slot_value", "bot_message"),
(
(
"can_withdraw",
False,
"You are not allowed to withdraw any amounts. Please check permission.",
),
(
"account_type",
"secondary",
"Withdrawal was sent for approval to primary account holder.",
),
),
)
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,
mappings=[{}],
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_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."
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."
@pytest.mark.parametrize(
("slot_name", "slot_value", "message"),
(
("test_bool", True, "example boolean"),
("test_int", 12, "example integer"),
("test_list", [], "example list"),
),
)
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,
mappings=[{}],
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_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"
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_with_slot_not_a_constraint():
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 = TextSlot(
name="account", mappings=[{}], 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_null_slot():
domain = Domain.from_yaml(
f"""
version: "{LATEST_TRAINING_DATA_FORMAT_VERSION}"
responses:
utter_action:
- text: "text for null"
condition:
- type: slot
name: account
value: null
"""
)
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.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"
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_variation_condition_met_channel_mismatch():
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: "app default"
channel: app
"""
)
t = TemplatedNaturalLanguageGenerator(domain.responses)
slot = TextSlot(
"test", mappings=[{}], initial_value="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"
@pytest.mark.parametrize(
"slots,channel,expected_response",
[
(
[
TextSlot(
"test",
mappings=[{}],
initial_value="B",
influence_conversation=False,
)
],
"app",
"condition example B no channel",
),
(
[
TextSlot(
"test",
mappings=[{}],
initial_value="C",
influence_conversation=False,
)
],
"",
"default",
),
(
[
TextSlot(
"test",
mappings=[{}],
initial_value="D",
influence_conversation=False,
)
],
"app",
"default",
),
],
)
async def test_nlg_conditional_edgecases(slots, channel, expected_response):
domain = Domain.from_yaml(
f"""
version: "{LATEST_TRAINING_DATA_FORMAT_VERSION}"
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_condition_logging(
caplog: LogCaptureFixture,
):
domain = Domain.from_yaml(
f"""
version: "{LATEST_TRAINING_DATA_FORMAT_VERSION}"
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
)