Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Raising exception when having empty intent or entity mapping in the domain #8222

Merged
merged 8 commits into from
Mar 25, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions changelog/7916.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Handle empty intent and entity mapping in the `domain`.

There is now an InvalidDomain exception raised if in the `domain.yml` file there are empty intent or entity mappings.
An example of empty intent and entity mappings is the following :
```yaml-rasa
intents:
- greet:
- goodbye:

entities:
- cuisine:
- number:
```
2 changes: 2 additions & 0 deletions rasa/shared/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
DOCS_URL_TRAINING_DATA_NLU = DOCS_URL_TRAINING_DATA + "#nlu-training-data"
DOCS_URL_DOMAINS = DOCS_BASE_URL + "/domain"
DOCS_URL_SLOTS = DOCS_URL_DOMAINS + "#slots"
DOCS_URL_INTENTS = DOCS_URL_DOMAINS + "#intents"
DOCS_URL_ENTITIES = DOCS_URL_DOMAINS + "#entities"
DOCS_URL_RESPONSES = DOCS_BASE_URL + "/responses"
DOCS_URL_STORIES = DOCS_BASE_URL + "/stories"
DOCS_URL_RULES = DOCS_BASE_URL + "/rules"
Expand Down
56 changes: 45 additions & 11 deletions rasa/shared/core/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,23 +312,44 @@ def _transform_intent_properties_for_internal_use(
roles: Dict[Text, List[Text]],
groups: Dict[Text, List[Text]],
) -> Dict[Text, Any]:
"""Transform intent properties coming from a domain file for internal use.
"""Transforms the intent's parameters in a format suitable for internal use.

In domain files, `use_entities` or `ignore_entities` is used. Internally, there
is a property `used_entities` instead that lists all entities to be used.
When an intent is retrieved from the `domain.yml` file, it contains two
parameters, the `use_entities` and the `ignore_entities` parameter. With
the values of these two parameters the Domain class is updated, a new
parameter is added to the intent called `used_entities` and the two
previous parameters are deleted. This happens because internally only the
parameter `used_entities` is needed to list all the entities that should be
used for this intent.

Args:
intent: The intents as provided by a domain file.
intent: The intent as retrieved from the `domain.yml` file thus having two
parameters, the `use_entities` and the `ignore_entities` parameter.
entities: All entities as provided by a domain file.
roles: All roles for entities as provided by a domain file.
groups: All groups for entities as provided by a domain file.

Returns:
The intents as they should be used internally.
The intent with the new format thus having only one parameter called
`used_entities` since this is the expected format of the intent
when used internally.
"""
name, properties = list(intent.items())[0]
Imod7 marked this conversation as resolved.
Show resolved Hide resolved

properties.setdefault(USE_ENTITIES_KEY, True)
if properties:
properties.setdefault(USE_ENTITIES_KEY, True)
else:
raise InvalidDomain(
f"In the `domain.yml` file, the intent '{name}' cannot have value of"
f" `{type(properties)}`. If you have placed a ':' character after the"
f" intent's name without adding any additional parameters to this"
f" intent then you would need to remove the ':' character. Please see"
f" {rasa.shared.constants.DOCS_URL_DOMAINS} for more information on how"
f" to correctly add `intents` in the `domain` and"
f" {rasa.shared.constants.DOCS_URL_INTENTS} for examples on"
f" when to use the ':' character after an intent's name."
)

properties.setdefault(IGNORE_ENTITIES_KEY, [])
if not properties[USE_ENTITIES_KEY]: # this covers False, None and []
properties[USE_ENTITIES_KEY] = []
Expand Down Expand Up @@ -403,17 +424,30 @@ def collect_entity_properties(
entities: List[Text] = []
roles: Dict[Text, List[Text]] = {}
groups: Dict[Text, List[Text]] = {}

for entity in domain_entities:
if isinstance(entity, str):
entities.append(entity)
elif isinstance(entity, dict):
for _entity, sub_labels in entity.items():
entities.append(_entity)
if ENTITY_ROLES_KEY in sub_labels:
roles[_entity] = sub_labels[ENTITY_ROLES_KEY]
if ENTITY_GROUPS_KEY in sub_labels:
groups[_entity] = sub_labels[ENTITY_GROUPS_KEY]
if sub_labels:
if ENTITY_ROLES_KEY in sub_labels:
roles[_entity] = sub_labels[ENTITY_ROLES_KEY]
if ENTITY_GROUPS_KEY in sub_labels:
groups[_entity] = sub_labels[ENTITY_GROUPS_KEY]
else:
raise InvalidDomain(
f"In the `domain.yml` file, the entity '{_entity}' cannot"
f" have value of `{type(sub_labels)}`. If you have placed a"
f" ':' character after the entity `{_entity}` without"
f" adding any additional parameters to this entity then you"
f" would need to remove the ':' character. Please see"
f" {rasa.shared.constants.DOCS_URL_DOMAINS} for more"
f" information on how to correctly add `entities` in the"
f" `domain` and {rasa.shared.constants.DOCS_URL_ENTITIES}"
f" for examples on when to use the ':' character after an"
f" entity's name."
)
else:
raise InvalidDomain(
f"Invalid domain. Entity is invalid, type of entity '{entity}' "
Expand Down
20 changes: 20 additions & 0 deletions tests/shared/core/test_domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -1313,3 +1313,23 @@ def test_is_valid_domain_doesnt_raise_with_invalid_yaml(tmpdir: Path):
potential_domain_path,
)
assert not Domain.is_domain_file(potential_domain_path)


def test_domain_with_empty_intent_mapping():
# domain.yml with intent (intent_name) that has a `:` character
# and nothing after it.
test_yaml = """intents:
- intent_name:"""

with pytest.raises(InvalidDomain):
Domain.from_yaml(test_yaml).as_dict()


def test_domain_with_empty_entity_mapping():
# domain.yml with entity (entity_name) that has a `:` character
# and nothing after it.
test_yaml = """entities:
- entity_name:"""

with pytest.raises(InvalidDomain):
Domain.from_yaml(test_yaml).as_dict()