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

940 lines
30 KiB
Python

from pathlib import Path
from typing import Text, List, Dict, Any
from unittest.mock import Mock
from _pytest.monkeypatch import MonkeyPatch
import pytest
import numpy as np
import rasa.shared.utils.io
from rasa.shared.core.constants import USER_INTENT_OUT_OF_SCOPE
from rasa.shared.nlu.constants import (
TEXT,
INTENT_RESPONSE_KEY,
ENTITY_ATTRIBUTE_START,
ENTITY_ATTRIBUTE_END,
ENTITY_ATTRIBUTE_VALUE,
ENTITY_ATTRIBUTE_TYPE,
ENTITIES,
INTENT,
ACTION_NAME,
FEATURE_TYPE_SENTENCE,
)
from rasa.nlu.convert import convert_training_data
from rasa.nlu.extractors.mitie_entity_extractor import MitieEntityExtractor
from rasa.nlu.tokenizers.whitespace_tokenizer import WhitespaceTokenizer
from rasa.shared.nlu.training_data.features import Features
from rasa.shared.nlu.training_data.message import Message
from rasa.shared.nlu.training_data.training_data import TrainingData
from rasa.shared.nlu.training_data.loading import guess_format, UNK, load_data
from rasa.shared.nlu.training_data.util import (
get_file_format_extension,
template_key_to_intent_response_key,
intent_response_key_to_template_key,
)
import rasa.shared.data
from rasa.shared.core.domain import Domain
from rasa.shared.core.events import UserUttered, ActionExecuted
from rasa.shared.core.training_data.structures import StoryGraph, StoryStep
from rasa.shared.importers.importer import TrainingDataImporter, E2EImporter
def test_luis_data():
td = load_data("data/examples/luis/demo-restaurants_v7.json")
assert not td.is_empty()
assert len(td.entity_examples) == 8
assert len(td.intent_examples) == 28
assert len(td.regex_features) == 1
assert len(td.training_examples) == 28
assert td.entity_synonyms == {}
assert td.intents == {"affirm", "goodbye", "greet", "inform"}
assert td.entities == {"location", "cuisine"}
def test_wit_data():
td = load_data("data/examples/wit/demo-flights.json")
assert not td.is_empty()
assert td.entity_examples == [
Message(
{
"intent": "flight_booking",
"entities": [
{
"entity": "location",
"start": 19,
"end": 25,
"entities": [],
"role": "from",
"value": "london",
}
],
"text": "i want to fly from london",
}
),
Message(
{
"intent": "flight_booking",
"entities": [
{
"entity": "location",
"start": 17,
"end": 23,
"entities": [],
"role": "to",
"value": "berlin",
}
],
"text": "i want to fly to berlin",
}
),
Message(
{
"intent": "flight_booking",
"entities": [
{
"entity": "location",
"start": 18,
"end": 24,
"entities": [],
"role": "from",
"value": "berlin",
},
{
"entity": "location",
"start": 28,
"end": 33,
"entities": [],
"role": "to",
"value": "tokyo",
},
],
"text": "i want to go from berlin to tokyo tomorrow",
}
),
Message(
{
"intent": "flight_booking",
"entities": [
{
"entity": "location",
"start": 30,
"end": 36,
"entities": [],
"role": "from",
"value": "london",
},
{
"entity": "wit$datetime",
"start": 50,
"end": 61,
"entities": [],
"role": "datetime",
"value": "next monday",
},
{
"entity": "location",
"start": 40,
"end": 49,
"entities": [],
"role": "to",
"value": "amsterdam",
},
],
"text": "i'm looking for a flight from london to amsterdam next monday",
}
),
]
assert len(td.intent_examples) == 5
assert len(td.training_examples) == 5
assert td.entity_synonyms == {}
assert td.intents == {"flight_booking", USER_INTENT_OUT_OF_SCOPE}
assert td.entities == {"location", "wit$datetime"}
def test_dialogflow_data():
td = load_data("data/examples/dialogflow/")
assert not td.is_empty()
assert len(td.entity_examples) == 5
assert len(td.intent_examples) == 24
assert len(td.training_examples) == 24
assert len(td.regex_features) == 1
assert len(td.lookup_tables) == 2
assert td.intents == {"affirm", "goodbye", "hi", "inform"}
assert td.entities == {"cuisine", "location"}
non_trivial_synonyms = {k: v for k, v in td.entity_synonyms.items() if k != v}
assert non_trivial_synonyms == {
"mexico": "mexican",
"china": "chinese",
"india": "indian",
}
# The order changes based on different computers hence the grouping
assert {td.lookup_tables[0]["name"], td.lookup_tables[1]["name"]} == {
"location",
"cuisine",
}
assert {
len(td.lookup_tables[0]["elements"]),
len(td.lookup_tables[1]["elements"]),
} == {4, 6}
def test_lookup_table_json():
lookup_fname = "data/test/lookup_tables/plates.txt"
td_lookup = load_data("data/test/lookup_tables/lookup_table.json")
assert not td_lookup.is_empty()
assert len(td_lookup.lookup_tables) == 1
assert td_lookup.lookup_tables[0]["name"] == "plates"
assert td_lookup.lookup_tables[0]["elements"] == lookup_fname
def test_lookup_table_yaml():
td_lookup = load_data("data/test/lookup_tables/lookup_table.yml")
assert not td_lookup.is_empty()
assert len(td_lookup.lookup_tables) == 1
assert td_lookup.lookup_tables[0]["name"] == "plates"
assert len(td_lookup.lookup_tables[0]["elements"]) == 5
def test_composite_entities_data():
td = load_data("data/test/demo-rasa-composite-entities.yml")
assert not td.is_empty()
assert len(td.entity_examples) == 11
assert len(td.intent_examples) == 29
assert len(td.training_examples) == 29
assert td.entity_synonyms == {"SF": "San Fransisco"}
assert td.intents == {"order_pizza", "book_flight", "chitchat", "affirm"}
assert td.entities == {"location", "topping", "size"}
assert td.entity_groups == {"1", "2"}
assert td.entity_roles == {"to", "from"}
assert td.number_of_examples_per_entity["entity 'location'"] == 8
assert td.number_of_examples_per_entity["group '1'"] == 9
assert td.number_of_examples_per_entity["role 'from'"] == 3
def test_intent_response_key_to_template_key():
intent_response_key = "chitchat/ask_name"
template_key = "utter_chitchat/ask_name"
assert intent_response_key_to_template_key(intent_response_key) == template_key
def test_template_key_to_intent_response_key():
intent_response_key = "chitchat/ask_name"
template_key = "utter_chitchat/ask_name"
assert template_key_to_intent_response_key(template_key) == intent_response_key
@pytest.mark.parametrize(
"files",
[
[
"data/examples/rasa/demo-rasa.json",
"data/examples/rasa/demo-rasa-responses.yml",
],
[
"data/examples/rasa/demo-rasa.yml",
"data/examples/rasa/demo-rasa-responses.yml",
],
],
)
def test_demo_data(files: List[Text]):
from rasa.shared.importers.utils import training_data_from_paths
trainingdata = training_data_from_paths(files, language="en")
assert trainingdata.intents == {
"affirm",
"greet",
"restaurant_search",
"goodbye",
"chitchat",
}
assert trainingdata.entities == {"location", "cuisine"}
assert set(trainingdata.responses.keys()) == {
"utter_chitchat/ask_name",
"utter_chitchat/ask_weather",
}
assert len(trainingdata.training_examples) == 46
assert len(trainingdata.intent_examples) == 46
assert len(trainingdata.response_examples) == 4
assert len(trainingdata.entity_examples) == 11
assert len(trainingdata.responses) == 2
assert trainingdata.entity_synonyms == {
"Chines": "chinese",
"Chinese": "chinese",
"chines": "chinese",
"vegg": "vegetarian",
"veggie": "vegetarian",
}
assert trainingdata.regex_features == [
{"name": "greet", "pattern": r"hey[^\s]*"},
{"name": "zipcode", "pattern": r"[0-9]{5}"},
]
@pytest.mark.parametrize(
"files",
[
[
"data/examples/rasa/demo-rasa.json",
"data/examples/rasa/demo-rasa-responses.yml",
],
[
"data/examples/rasa/demo-rasa.yml",
"data/examples/rasa/demo-rasa-responses.yml",
],
],
)
def test_demo_data_filter_out_retrieval_intents(files):
from rasa.shared.importers.utils import training_data_from_paths
training_data = training_data_from_paths(files, language="en")
assert len(training_data.training_examples) == 46
training_data_filtered = training_data.filter_training_examples(
lambda ex: ex.get(INTENT_RESPONSE_KEY) is None
)
assert len(training_data_filtered.training_examples) == 42
training_data_filtered_2 = training_data.filter_training_examples(
lambda ex: ex.get(INTENT_RESPONSE_KEY) is not None
)
assert len(training_data_filtered_2.training_examples) == 4
# make sure filtering operation doesn't mutate the source training data
assert len(training_data.training_examples) == 46
@pytest.mark.parametrize(
"filepaths",
[
[
"data/examples/rasa/demo-rasa.yml",
"data/examples/rasa/demo-rasa-responses.yml",
]
],
)
def test_train_test_split(filepaths: List[Text]):
from rasa.shared.importers.utils import training_data_from_paths
training_data = training_data_from_paths(filepaths, language="en")
assert training_data.intents == {
"affirm",
"greet",
"restaurant_search",
"goodbye",
"chitchat",
}
assert training_data.entities == {"location", "cuisine"}
assert set(training_data.responses.keys()) == {
"utter_chitchat/ask_name",
"utter_chitchat/ask_weather",
}
NUM_TRAIN_EXAMPLES = 46
NUM_RESPONSE_EXAMPLES = 4
assert len(training_data.training_examples) == NUM_TRAIN_EXAMPLES
assert len(training_data.intent_examples) == NUM_TRAIN_EXAMPLES
assert len(training_data.response_examples) == NUM_RESPONSE_EXAMPLES
for train_percent in range(50, 95, 5):
train_frac = train_percent / 100.0
train_split, test_split = training_data.train_test_split(train_frac)
assert (
len(test_split.training_examples) + len(train_split.training_examples)
== NUM_TRAIN_EXAMPLES
)
num_classes = (
len(training_data.number_of_examples_per_intent.keys())
+ -len(training_data.retrieval_intents)
+ len(training_data.number_of_examples_per_response)
)
expected_num_train_examples_floor = int(train_frac * NUM_TRAIN_EXAMPLES)
if NUM_TRAIN_EXAMPLES - expected_num_train_examples_floor < num_classes:
expected_num_train_examples_floor = NUM_TRAIN_EXAMPLES - num_classes - 1
assert len(train_split.training_examples) >= expected_num_train_examples_floor
assert (
len(train_split.training_examples) <= expected_num_train_examples_floor + 1
)
assert len(training_data.number_of_examples_per_intent.keys()) == len(
test_split.number_of_examples_per_intent.keys()
)
assert len(training_data.number_of_examples_per_intent.keys()) == len(
train_split.number_of_examples_per_intent.keys()
)
assert len(training_data.number_of_examples_per_response.keys()) == len(
train_split.number_of_examples_per_response.keys()
)
assert len(training_data.number_of_examples_per_response.keys()) == len(
train_split.number_of_examples_per_response.keys()
)
def test_number_of_examples_per_intent():
message_action = Message(data={"action_name": "utter_greet"})
message_intent = Message(
data={"text": "I would like the newsletter", "intent": "subscribe"}
)
message_non_nlu_intent = Message(data={"intent": "subscribe"})
message_other_intent_one = Message(
data={"text": "What is the weather like today?", "intent": "ask_weather"}
)
message_other_intent_two = Message(
data={"text": "Will it rain today?", "intent": "ask_weather"}
)
message_non_nlu_other_intent_three = Message(data={"intent": "ask_weather"})
training_examples = [
message_action,
message_intent,
message_non_nlu_intent,
message_other_intent_one,
message_other_intent_two,
message_non_nlu_other_intent_three,
]
training_data = TrainingData(training_examples=training_examples)
assert training_data.number_of_examples_per_intent["subscribe"] == 1
assert training_data.number_of_examples_per_intent["ask_weather"] == 2
def test_number_of_examples_per_intent_with_yaml(tmp_path: Path):
domain_path = tmp_path / "domain.yml"
domain_path.write_text(Domain.empty().as_yaml())
config_path = tmp_path / "config.yml"
config_path.touch()
importer = TrainingDataImporter.load_from_dict(
{},
str(config_path),
str(domain_path),
[
"data/test_number_nlu_examples/nlu.yml",
"data/test_number_nlu_examples/stories.yml",
"data/test_number_nlu_examples/rules.yml",
],
)
training_data = importer.get_nlu_data()
assert training_data.intents == {"greet", "ask_weather"}
assert training_data.number_of_examples_per_intent["greet"] == 2
assert training_data.number_of_examples_per_intent["ask_weather"] == 3
def test_validate_number_of_examples_per_intent():
message_intent = Message(
data={"text": "I would like the newsletter", "intent": "subscribe"}
)
message_non_nlu_intent = Message(data={"intent": "subscribe"})
training_examples = [message_intent, message_non_nlu_intent]
training_data = TrainingData(training_examples=training_examples)
with pytest.warns(Warning) as w:
training_data.validate()
assert len(w) == 1
assert (
w[0].message.args[0] == "Intent 'subscribe' has only 1 training examples! "
"Minimum is 2, training may fail."
)
@pytest.mark.parametrize(
"filepaths",
[
[
"data/examples/rasa/demo-rasa.yml",
"data/examples/rasa/demo-rasa-responses.yml",
]
],
)
def test_train_test_split_with_random_seed(filepaths):
from rasa.shared.importers.utils import training_data_from_paths
td = training_data_from_paths(filepaths, language="en")
td_train_1, td_test_1 = td.train_test_split(train_frac=0.8, random_seed=1)
td_train_2, td_test_2 = td.train_test_split(train_frac=0.8, random_seed=1)
train_1_intent_examples = [e.get(TEXT) for e in td_train_1.intent_examples]
train_2_intent_examples = [e.get(TEXT) for e in td_train_2.intent_examples]
test_1_intent_examples = [e.get(TEXT) for e in td_test_1.intent_examples]
test_2_intent_examples = [e.get(TEXT) for e in td_test_2.intent_examples]
assert train_1_intent_examples == train_2_intent_examples
assert test_1_intent_examples == test_2_intent_examples
@pytest.mark.parametrize(
"files",
[
("data/examples/rasa/demo-rasa.json", "data/test/multiple_files_json"),
("data/examples/rasa/demo-rasa.yml", "data/test/duplicate_intents_yaml"),
],
)
def test_data_merging(files):
td_reference = load_data(files[0])
td = load_data(files[1])
assert len(td.entity_examples) == len(td_reference.entity_examples)
assert len(td.intent_examples) == len(td_reference.intent_examples)
assert len(td.training_examples) == len(td_reference.training_examples)
assert td.intents == td_reference.intents
assert td.entities == td_reference.entities
assert td.entity_synonyms == td_reference.entity_synonyms
assert td.regex_features == td_reference.regex_features
def test_repeated_entities(tmp_path: Path, whitespace_tokenizer: WhitespaceTokenizer):
data = """
{
"rasa_nlu_data": {
"common_examples" : [
{
"text": "book a table today from 3 to 6 for 3 people",
"intent": "unk",
"entities": [
{
"entity": "description",
"start": 35,
"end": 36,
"value": "3"
}
]
}
]
}
}"""
f = tmp_path / "tmp_training_data.json"
f.write_text(data, rasa.shared.utils.io.DEFAULT_ENCODING)
td = load_data(str(f))
assert len(td.entity_examples) == 1
example = td.entity_examples[0]
entities = example.get("entities")
assert len(entities) == 1
tokens = whitespace_tokenizer.tokenize(example, attribute=TEXT)
start, end = MitieEntityExtractor.find_entity(
entities[0], example.get(TEXT), tokens
)
assert start == 9
assert end == 10
def test_multiword_entities(tmp_path: Path, whitespace_tokenizer: WhitespaceTokenizer):
data = """
{
"rasa_nlu_data": {
"common_examples" : [
{
"text": "show me flights to New York City",
"intent": "unk",
"entities": [
{
"entity": "destination",
"start": 19,
"end": 32,
"value": "New York City"
}
]
}
]
}
}"""
f = tmp_path / "tmp_training_data.json"
f.write_text(data, rasa.shared.utils.io.DEFAULT_ENCODING)
td = load_data(str(f))
assert len(td.entity_examples) == 1
example = td.entity_examples[0]
entities = example.get("entities")
assert len(entities) == 1
tokens = whitespace_tokenizer.tokenize(example, attribute=TEXT)
start, end = MitieEntityExtractor.find_entity(
entities[0], example.get(TEXT), tokens
)
assert start == 4
assert end == 7
def test_nonascii_entities(tmp_path):
data = """
{
"luis_schema_version": "7.0",
"utterances" : [
{
"text": "I am looking for a ßäæ ?€ö) item",
"intent": "unk",
"entities": [
{
"entity": "description",
"startPos": 19,
"endPos": 26
}
]
}
]
}"""
f = tmp_path / "tmp_training_data.json"
f.write_text(data, rasa.shared.utils.io.DEFAULT_ENCODING)
td = load_data(str(f))
assert len(td.entity_examples) == 1
example = td.entity_examples[0]
entities = example.get(ENTITIES)
assert len(entities) == 1
entity = entities[0]
assert entity[ENTITY_ATTRIBUTE_VALUE] == "ßäæ ?€ö)"
assert entity[ENTITY_ATTRIBUTE_START] == 19
assert entity[ENTITY_ATTRIBUTE_END] == 27
assert entity[ENTITY_ATTRIBUTE_TYPE] == "description"
def test_entities_synonyms(tmp_path):
data = """
{
"rasa_nlu_data": {
"entity_synonyms": [
{
"value": "nyc",
"synonyms": ["New York City", "nyc", "the big apple"]
}
],
"common_examples" : [
{
"text": "show me flights to New York City",
"intent": "unk",
"entities": [
{
"entity": "destination",
"start": 19,
"end": 32,
"value": "NYC"
}
]
},
{
"text": "show me flights to nyc",
"intent": "unk",
"entities": [
{
"entity": "destination",
"start": 19,
"end": 22,
"value": "nyc"
}
]
}
]
}
}"""
f = tmp_path / "tmp_training_data.json"
f.write_text(data, rasa.shared.utils.io.DEFAULT_ENCODING)
td = load_data(str(f))
assert td.entity_synonyms["New York City"] == "nyc"
def cmp_message_list(firsts, seconds):
assert len(firsts) == len(seconds), "Message lists have unequal length"
def cmp_dict_list(firsts, seconds):
if len(firsts) != len(seconds):
return False
for a in firsts:
for idx, b in enumerate(seconds):
if hash(a) == hash(b):
del seconds[idx]
break
else:
others = ", ".join(e.text for e in seconds)
assert False, f"Failed to find message {a.text} in {others}"
return not seconds
@pytest.mark.parametrize(
"data_file,gold_standard_file,output_format,language",
[
(
"data/examples/wit/demo-flights.json",
"data/test/wit_converted_to_rasa.json",
"json",
None,
),
(
"data/examples/luis/demo-restaurants_v7.json",
"data/test/luis_converted_to_rasa.json",
"json",
None,
),
(
"data/examples/dialogflow/",
"data/test/dialogflow_en_converted_to_rasa.json",
"json",
"en",
),
(
"data/examples/dialogflow/",
"data/test/dialogflow_es_converted_to_rasa.json",
"json",
"es",
),
(
"data/examples/rasa/demo-rasa.yml",
"data/test/md_converted_to_json.json",
"json",
None,
),
],
)
def test_training_data_conversion(
tmpdir, data_file, gold_standard_file, output_format, language
):
out_path = tmpdir.join("rasa_nlu_data.json")
convert_training_data(data_file, out_path.strpath, output_format, language)
td = load_data(out_path.strpath, language)
assert td.entity_examples != []
assert td.intent_examples != []
gold_standard = load_data(gold_standard_file, language)
cmp_message_list(td.entity_examples, gold_standard.entity_examples)
cmp_message_list(td.intent_examples, gold_standard.intent_examples)
assert td.entity_synonyms == gold_standard.entity_synonyms
assert td.entity_roles == gold_standard.entity_roles
# converting the converted file back to original
# file format and performing the same tests
rto_path = tmpdir.join("data_in_original_format.txt")
convert_training_data(out_path.strpath, rto_path.strpath, "json", language)
rto = load_data(rto_path.strpath, language)
cmp_message_list(gold_standard.entity_examples, rto.entity_examples)
cmp_message_list(gold_standard.intent_examples, rto.intent_examples)
assert gold_standard.entity_synonyms == rto.entity_synonyms
# If the above assert fails - this can be used
# to dump to the file and diff using git
# with io.open(gold_standard_file) as f:
# f.write(td.as_json(indent=2))
@pytest.mark.parametrize(
"data_file,expected_format",
[
(
"data/examples/luis/demo-restaurants_v7.json",
rasa.shared.data.yaml_file_extension(),
),
("data/examples", rasa.shared.data.yaml_file_extension()),
("data/examples/rasa/demo-rasa.yml", rasa.shared.data.yaml_file_extension()),
("data/rasa_yaml_examples", rasa.shared.data.yaml_file_extension()),
],
)
def test_get_supported_file_format(data_file: Text, expected_format: Text):
fformat = get_file_format_extension(data_file)
assert fformat == expected_format
@pytest.mark.parametrize("data_file", ["path-does-not-exists", None])
def test_get_non_existing_file_format_raises(data_file: Text):
with pytest.raises(AttributeError):
get_file_format_extension(data_file)
def test_guess_format_from_non_existing_file_path():
assert guess_format("not existing path") == UNK
def test_is_empty():
assert TrainingData().is_empty()
def test_custom_attributes(tmp_path):
data = """
{
"rasa_nlu_data": {
"common_examples" : [
{
"intent": "happy",
"text": "I'm happy.",
"sentiment": 0.8
}
]
}
}"""
f = tmp_path / "tmp_training_data.json"
f.write_text(data, rasa.shared.utils.io.DEFAULT_ENCODING)
td = load_data(str(f))
assert len(td.training_examples) == 1
example = td.training_examples[0]
assert example.get("sentiment") == 0.8
def test_without_additional_e2e_examples(tmp_path: Path):
domain_path = tmp_path / "domain.yml"
domain_path.write_text(Domain.empty().as_yaml())
config_path = tmp_path / "config.yml"
config_path.touch()
existing = TrainingDataImporter.load_from_dict(
{}, str(config_path), str(domain_path), []
)
stories = StoryGraph(
[
StoryStep(
"name",
events=[
UserUttered(None, {"name": "greet_from_stories"}),
ActionExecuted("utter_greet_from_stories"),
],
)
]
)
# Patch to return our test stories
existing.get_stories = lambda *args: stories
importer = E2EImporter(existing)
training_data = importer.get_nlu_data()
assert training_data.training_examples
assert not training_data.is_empty()
assert len(training_data.nlu_examples) == 0
@pytest.mark.parametrize(
"source_lookup_table,expected_lookup_table",
[
(
{"name": "plates", "elements": "data/test/lookup_tables/plates.txt"},
{
"name": "plates",
"elements": "tacos\nbeef\nmapo tofu\nburrito\nlettuce wrap",
},
),
(
{"name": "plates", "elements": ["data/test/lookup_tables/plates.txt"]},
{
"name": "plates",
"elements": "tacos\nbeef\nmapo tofu\nburrito\nlettuce wrap",
},
),
(
{
"name": "plates",
"elements": "data/test/lookup_tables/not-existing-file.txt",
},
{
"name": "plates",
"elements": "data/test/lookup_tables/not-existing-file.txt",
},
),
(
{"name": "test", "some_key": "some_value", "elements": "everything else"},
{"name": "test", "some_key": "some_value", "elements": "everything else"},
),
],
)
def test_load_lookup_table(
source_lookup_table: Dict[Text, Any], expected_lookup_table: Dict[Text, Any]
):
assert TrainingData._load_lookup_table(source_lookup_table) == expected_lookup_table
def test_fingerprint_is_same_when_loading_data_again():
from rasa.shared.importers.utils import training_data_from_paths
files = [
"data/examples/rasa/demo-rasa.yml",
"data/examples/rasa/demo-rasa-responses.yml",
]
td1 = training_data_from_paths(files, language="en")
td2 = training_data_from_paths(files, language="en")
assert td1.fingerprint() == td2.fingerprint()
def test_fingerprint_is_different_when_lookup_table_has_changed(
monkeypatch: MonkeyPatch,
):
from rasa.shared.importers.utils import training_data_from_paths
files = ["data/test/lookup_tables/lookup_table.json"]
td1 = training_data_from_paths(files, language="en")
fingerprint1 = td1.fingerprint()
monkeypatch.setattr(
TrainingData,
"_load_lookup_table",
Mock(return_value={"name": "plates", "elements": "tacos\nbeef"}),
)
td2 = training_data_from_paths(files, language="en")
fingerprint2 = td2.fingerprint()
assert fingerprint1 != fingerprint2
@pytest.mark.parametrize(
"message",
[
Message({INTENT: "intent2"}),
Message({ENTITIES: [{"entity": "entity2"}]}),
Message({ENTITIES: [{"entity": "entity1", "group": "new_group"}]}),
Message({ENTITIES: [{"entity": "entity1", "role": "new_role"}]}),
Message({ACTION_NAME: "action_name2"}),
],
)
def test_label_fingerprints(message: Message):
training_data1 = TrainingData(
[
Message({INTENT: "intent1"}),
Message({ENTITIES: [{"entity": "entity1"}]}),
Message({ACTION_NAME: "action_name1"}),
]
)
training_data2 = training_data1.merge(TrainingData([message]))
assert training_data1.label_fingerprint() != training_data2.label_fingerprint()
def test_training_data_fingerprint_incorporates_tokens(
whitespace_tokenizer: WhitespaceTokenizer,
):
from rasa.shared.importers.utils import training_data_from_paths
files = [
"data/examples/rasa/demo-rasa.yml",
"data/examples/rasa/demo-rasa-responses.yml",
]
training_data = training_data_from_paths(files, language="en")
fp1 = training_data.fingerprint()
whitespace_tokenizer.process_training_data(training_data)
# training data fingerprint has changed
assert fp1 != training_data.fingerprint()
def test_training_data_fingerprint_incorporates_features():
from rasa.shared.importers.utils import training_data_from_paths
files = [
"data/examples/rasa/demo-rasa.yml",
"data/examples/rasa/demo-rasa-responses.yml",
]
training_data = training_data_from_paths(files, language="en")
fp1 = training_data.fingerprint()
big_array = np.random.random((128, 128))
f1 = Features(big_array, FEATURE_TYPE_SENTENCE, TEXT, "RegexFeaturizer")
training_data.training_examples[0].add_features(f1)
# training data fingerprint has changed
assert fp1 != training_data.fingerprint()