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
398 lines
13 KiB
Python
398 lines
13 KiB
Python
from typing import Any, Tuple, List, Dict, Text
|
|
|
|
import pytest
|
|
from _pytest.fixtures import SubRequest
|
|
|
|
import rasa.shared.core.constants
|
|
from rasa.shared.core.events import SlotSet
|
|
from rasa.shared.core.slots import (
|
|
InvalidSlotTypeException,
|
|
Slot,
|
|
TextSlot,
|
|
BooleanSlot,
|
|
FloatSlot,
|
|
ListSlot,
|
|
CategoricalSlot,
|
|
bool_from_any,
|
|
AnySlot,
|
|
InvalidSlotConfigError,
|
|
)
|
|
from rasa.shared.core.trackers import DialogueStateTracker
|
|
|
|
|
|
class SlotTestCollection:
|
|
"""Tests every slot needs to fulfill.
|
|
|
|
Each slot can declare further tests on its own."""
|
|
|
|
def create_slot(
|
|
self, mappings: List[Dict[Text, Any]], influence_conversation: bool
|
|
) -> Slot:
|
|
raise NotImplementedError
|
|
|
|
def value_feature_pair(self, request: SubRequest) -> Tuple[Any, List[float]]:
|
|
"""Values where featurization is defined and should be tested."""
|
|
raise NotImplementedError
|
|
|
|
def invalid_value(self, request: SubRequest) -> Any:
|
|
"""Values, that should be handled gracefully but where the
|
|
featurization is not defined."""
|
|
raise NotImplementedError
|
|
|
|
@pytest.fixture()
|
|
def mappings(self) -> List[Dict[Text, Any]]:
|
|
return [{}]
|
|
|
|
def test_featurization(
|
|
self,
|
|
value_feature_pair: Tuple[Any, List[float]],
|
|
mappings: List[Dict[Text, Any]],
|
|
):
|
|
slot = self.create_slot(mappings=mappings, influence_conversation=True)
|
|
value, expected = value_feature_pair
|
|
slot.value = value
|
|
assert slot.as_feature() == expected
|
|
assert (
|
|
len(slot.as_feature()) == slot.feature_dimensionality()
|
|
), "Wrong feature dimensionality"
|
|
|
|
# now reset the slot to get initial value again
|
|
slot.reset()
|
|
assert (
|
|
slot.value == slot.initial_value
|
|
), "Slot should be reset to its initial value"
|
|
|
|
def test_empty_slot_featurization(self, mappings: List[Dict[Text, Any]]):
|
|
slot = self.create_slot(mappings=mappings, influence_conversation=True)
|
|
assert (
|
|
slot.value == slot.initial_value
|
|
), "An empty slot should be set to the initial value"
|
|
assert len(slot.as_feature()) == slot.feature_dimensionality()
|
|
|
|
def test_featurization_if_marked_as_unfeaturized(
|
|
self,
|
|
value_feature_pair: Tuple[Any, List[float]],
|
|
mappings: List[Dict[Text, Any]],
|
|
):
|
|
slot = self.create_slot(mappings=mappings, influence_conversation=False)
|
|
value, _ = value_feature_pair
|
|
slot.value = value
|
|
|
|
features = slot.as_feature()
|
|
assert features == []
|
|
|
|
dimensions = slot.feature_dimensionality()
|
|
assert dimensions == 0
|
|
|
|
def test_has_a_type_name(self, mappings: List[Dict[Text, Any]]):
|
|
slot = self.create_slot(mappings=mappings, influence_conversation=True)
|
|
assert slot.type_name is not None
|
|
assert type(slot) == Slot.resolve_by_type(slot.type_name)
|
|
|
|
def test_handles_invalid_values(
|
|
self, invalid_value: Any, mappings: List[Dict[Text, Any]]
|
|
):
|
|
slot = self.create_slot(mappings=mappings, influence_conversation=True)
|
|
slot.value = invalid_value
|
|
assert slot.as_feature() is not None
|
|
assert len(slot.as_feature()) == slot.feature_dimensionality()
|
|
|
|
@pytest.mark.parametrize("influence_conversation", [True, False])
|
|
def test_serialization(
|
|
self, influence_conversation: bool, mappings: List[Dict[Text, Any]]
|
|
):
|
|
slot = self.create_slot(mappings, influence_conversation)
|
|
|
|
persistence_info = slot.persistence_info()
|
|
|
|
slot_type = Slot.resolve_by_type(persistence_info.get("type"))
|
|
recreated = slot_type(
|
|
slot.name, **{k: v for k, v in persistence_info.items() if k != "type"}
|
|
)
|
|
|
|
assert isinstance(slot, slot_type)
|
|
assert recreated.persistence_info() == persistence_info
|
|
|
|
@pytest.mark.parametrize("influence_conversation", [True, False])
|
|
def test_slot_has_been_set(
|
|
self,
|
|
influence_conversation: bool,
|
|
value_feature_pair: Tuple[Any, List[float]],
|
|
mappings: List[Dict[Text, Any]],
|
|
):
|
|
slot = self.create_slot(mappings, influence_conversation)
|
|
assert not slot.has_been_set
|
|
value, _ = value_feature_pair
|
|
slot.value = value
|
|
assert slot.has_been_set
|
|
slot.reset()
|
|
assert not slot.has_been_set
|
|
|
|
@pytest.mark.parametrize(
|
|
"influence_conversation, slot_mappings",
|
|
[
|
|
(True, []),
|
|
(True, [{"type": "from_entity", "entity": "test"}]),
|
|
(False, []),
|
|
(False, [{"type": "from_entity", "entity": "test"}]),
|
|
],
|
|
)
|
|
def test_slot_fingerprint_consistency(
|
|
self, influence_conversation: bool, slot_mappings: List[Dict[Text, Any]]
|
|
):
|
|
slot1 = self.create_slot(slot_mappings, influence_conversation)
|
|
slot2 = self.create_slot(slot_mappings, influence_conversation)
|
|
f1 = slot1.fingerprint()
|
|
f2 = slot2.fingerprint()
|
|
assert f1 == f2
|
|
|
|
@pytest.mark.parametrize("influence_conversation", [True, False])
|
|
def test_slot_fingerprint_uniqueness(
|
|
self, influence_conversation: bool, mappings: List[Dict[Text, Any]]
|
|
):
|
|
slot = self.create_slot(mappings, influence_conversation)
|
|
f1 = slot.fingerprint()
|
|
slot.value = "changed"
|
|
f2 = slot.fingerprint()
|
|
assert f1 != f2
|
|
|
|
|
|
class TestTextSlot(SlotTestCollection):
|
|
def create_slot(
|
|
self, mappings: List[Dict[Text, Any]], influence_conversation: bool
|
|
) -> Slot:
|
|
return TextSlot(
|
|
"test", mappings=mappings, influence_conversation=influence_conversation
|
|
)
|
|
|
|
@pytest.fixture(params=[1, {"a": "b"}, 2.0, [], True])
|
|
def invalid_value(self, request: SubRequest) -> Any:
|
|
return request.param
|
|
|
|
@pytest.fixture(
|
|
params=[
|
|
(None, [0]),
|
|
("", [1]),
|
|
("some test string", [1]),
|
|
("some test string 🌴", [1]),
|
|
]
|
|
)
|
|
def value_feature_pair(self, request: SubRequest) -> Tuple[Any, List[float]]:
|
|
return request.param
|
|
|
|
|
|
class TestBooleanSlot(SlotTestCollection):
|
|
def create_slot(
|
|
self, mappings: List[Dict[Text, Any]], influence_conversation: bool
|
|
) -> Slot:
|
|
return BooleanSlot(
|
|
"test", mappings=mappings, influence_conversation=influence_conversation
|
|
)
|
|
|
|
@pytest.fixture(params=[{"a": "b"}, [], "asd", "🌴"])
|
|
def invalid_value(self, request: SubRequest) -> Any:
|
|
return request.param
|
|
|
|
@pytest.fixture(
|
|
params=[
|
|
(None, [0, 0]),
|
|
(True, [1, 1]),
|
|
("9", [1, 0]),
|
|
(12, [1, 0]),
|
|
(False, [1, 0]),
|
|
("0", [1, 0]),
|
|
(0, [1, 0]),
|
|
("true", [1, 1]),
|
|
("True", [1, 1]),
|
|
("false", [1, 0]),
|
|
("False", [1, 0]),
|
|
]
|
|
)
|
|
def value_feature_pair(self, request: SubRequest) -> Tuple[Any, List[float]]:
|
|
return request.param
|
|
|
|
|
|
def test_bool_from_any_raises_value_error():
|
|
with pytest.raises(ValueError):
|
|
bool_from_any("abc")
|
|
|
|
|
|
def test_bool_from_any_raises_type_error():
|
|
with pytest.raises(TypeError):
|
|
bool_from_any(None)
|
|
|
|
|
|
class TestFloatSlot(SlotTestCollection):
|
|
def create_slot(
|
|
self, mappings: List[Dict[Text, Any]], influence_conversation: bool = False
|
|
) -> Slot:
|
|
return FloatSlot(
|
|
"test", mappings=mappings, influence_conversation=influence_conversation
|
|
)
|
|
|
|
@pytest.fixture(params=[{"a": "b"}, [], "asd", "🌴"])
|
|
def invalid_value(self, request: SubRequest) -> Any:
|
|
return request.param
|
|
|
|
@pytest.fixture(
|
|
params=[
|
|
(None, [0, 0]),
|
|
(True, [1, 1]),
|
|
(2.0, [1, 1]),
|
|
(1.0, [1, 1]),
|
|
(0.5, [1, 0.5]),
|
|
(0, [1, 0]),
|
|
(-0.5, [1, 0.0]),
|
|
]
|
|
)
|
|
def value_feature_pair(self, request: SubRequest) -> Tuple[Any, List[float]]:
|
|
return request.param
|
|
|
|
|
|
class TestListSlot(SlotTestCollection):
|
|
def create_slot(
|
|
self, mappings: List[Dict[Text, Any]], influence_conversation: bool
|
|
) -> Slot:
|
|
return ListSlot(
|
|
"test", mappings=mappings, influence_conversation=influence_conversation
|
|
)
|
|
|
|
@pytest.fixture(params=[{"a": "b"}, 1, True, "asd", "🌴"])
|
|
def invalid_value(self, request: SubRequest) -> Any:
|
|
return request.param
|
|
|
|
@pytest.fixture(params=[(None, [0]), ([], [0]), ([1], [1]), (["asd", 1, {}], [1])])
|
|
def value_feature_pair(self, request: SubRequest) -> Tuple[Any, List[float]]:
|
|
return request.param
|
|
|
|
@pytest.mark.parametrize("value", ["cat", ["cat"]])
|
|
def test_apply_single_item_to_slot(
|
|
self, value: Any, mappings: List[Dict[Text, Any]]
|
|
):
|
|
slot = self.create_slot(mappings=mappings, influence_conversation=False)
|
|
tracker = DialogueStateTracker.from_events("sender", evts=[], slots=[slot])
|
|
|
|
slot_event = SlotSet(slot.name, value)
|
|
tracker.update(slot_event)
|
|
|
|
assert tracker.slots[slot.name].value == ["cat"]
|
|
|
|
|
|
class TestCategoricalSlot(SlotTestCollection):
|
|
def create_slot(
|
|
self, mappings: List[Dict[Text, Any]], influence_conversation: bool
|
|
) -> Slot:
|
|
return CategoricalSlot(
|
|
"test",
|
|
mappings=mappings,
|
|
values=[1, "two", "小于", {"three": 3}, "nOnE", "None", "null"],
|
|
influence_conversation=influence_conversation,
|
|
)
|
|
|
|
# None is a special value reserved for unset slots.
|
|
@pytest.fixture(params=[{"a": "b"}, 2, True, "asd", "🌴", None])
|
|
def invalid_value(self, request: SubRequest) -> Any:
|
|
return request.param
|
|
|
|
@pytest.fixture(
|
|
params=[
|
|
(None, [0, 0, 0, 0, 0, 0, 0]), # slot is unset
|
|
(1, [1, 0, 0, 0, 0, 0, 0]),
|
|
("two", [0, 1, 0, 0, 0, 0, 0]),
|
|
("小于", [0, 0, 1, 0, 0, 0, 0]),
|
|
({"three": 3}, [0, 0, 0, 1, 0, 0, 0]),
|
|
("nOnE", [0, 0, 0, 0, 1, 0, 0]),
|
|
("None", [0, 0, 0, 0, 1, 0, 0]), # same as for 'nOnE' (case insensivity)
|
|
("null", [0, 0, 0, 0, 0, 0, 1]),
|
|
(
|
|
rasa.shared.core.constants.DEFAULT_CATEGORICAL_SLOT_VALUE,
|
|
[0, 0, 0, 0, 0, 0, 0],
|
|
),
|
|
]
|
|
)
|
|
def value_feature_pair(self, request: SubRequest) -> Tuple[Any, List[float]]:
|
|
return request.param
|
|
|
|
|
|
class TestCategoricalSlotDefaultValue(SlotTestCollection):
|
|
def create_slot(
|
|
self, mappings: List[Dict[Text, Any]], influence_conversation: bool
|
|
) -> Slot:
|
|
slot = CategoricalSlot(
|
|
"test",
|
|
mappings=mappings,
|
|
values=[1, "two", "小于", {"three": 3}, "nOnE", "None", "null"],
|
|
influence_conversation=influence_conversation,
|
|
)
|
|
slot.add_default_value()
|
|
return slot
|
|
|
|
# None is a special value reserved for unset slots.
|
|
@pytest.fixture(params=[{"a": "b"}, 2, True, "asd", "🌴", None])
|
|
def invalid_value(self, request: SubRequest) -> Any:
|
|
return request.param
|
|
|
|
@pytest.fixture(
|
|
params=[
|
|
(None, [0, 0, 0, 0, 0, 0, 0, 0]), # slot is unset
|
|
(1, [1, 0, 0, 0, 0, 0, 0, 0]),
|
|
("two", [0, 1, 0, 0, 0, 0, 0, 0]),
|
|
("小于", [0, 0, 1, 0, 0, 0, 0, 0]),
|
|
({"three": 3}, [0, 0, 0, 1, 0, 0, 0, 0]),
|
|
("nOnE", [0, 0, 0, 0, 1, 0, 0, 0]),
|
|
("None", [0, 0, 0, 0, 1, 0, 0, 0]), # same as for 'nOnE' (case insensivity)
|
|
("null", [0, 0, 0, 0, 0, 0, 1, 0]),
|
|
(
|
|
rasa.shared.core.constants.DEFAULT_CATEGORICAL_SLOT_VALUE,
|
|
[0, 0, 0, 0, 0, 0, 0, 1],
|
|
),
|
|
("unseen value", [0, 0, 0, 0, 0, 0, 0, 1]),
|
|
]
|
|
)
|
|
def value_feature_pair(self, request: SubRequest) -> Tuple[Any, List[float]]:
|
|
return request.param
|
|
|
|
|
|
class TestAnySlot(SlotTestCollection):
|
|
def create_slot(
|
|
self, mappings: List[Dict[Text, Any]], influence_conversation: bool
|
|
) -> Slot:
|
|
return AnySlot("test", mappings=mappings, influence_conversation=False)
|
|
|
|
@pytest.fixture(params=["there is nothing invalid, but we need to pass something"])
|
|
def invalid_value(self, request: SubRequest) -> Any:
|
|
return request.param
|
|
|
|
@pytest.fixture(
|
|
params=[
|
|
(None, []),
|
|
([], []),
|
|
({"nested": {"dict": [1, 2, 3]}}, []),
|
|
(["asd", 1, {}], []),
|
|
]
|
|
)
|
|
def value_feature_pair(self, request: SubRequest) -> Tuple[Any, List[float]]:
|
|
return request.param
|
|
|
|
def test_exception_if_featurized(self, mappings: List[Dict[Text, Any]]):
|
|
with pytest.raises(InvalidSlotConfigError):
|
|
AnySlot("⛔️", mappings=mappings, influence_conversation=True)
|
|
|
|
|
|
def test_raises_on_invalid_slot_type():
|
|
with pytest.raises(InvalidSlotTypeException):
|
|
Slot.resolve_by_type("foobar")
|
|
|
|
|
|
def test_categorical_slot_ignores_none_value():
|
|
"""Checks that None can't be added as a possible value for categorical slots."""
|
|
with pytest.warns(UserWarning) as records:
|
|
slot = CategoricalSlot(
|
|
name="branch", mappings=[{}], values=["Berlin", None, "San Francisco"]
|
|
)
|
|
|
|
assert "none" not in slot.values
|
|
|
|
message_text = "Rasa will ignore `null` as a possible value for the 'branch' slot."
|
|
assert any(message_text in record.message.args[0] for record in records)
|