rasa-pro 3.9.18__py3-none-any.whl → 3.10.16__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rasa-pro might be problematic. Click here for more details.
- README.md +0 -374
- rasa/__init__.py +1 -2
- rasa/__main__.py +5 -0
- rasa/anonymization/anonymization_rule_executor.py +2 -2
- rasa/api.py +27 -23
- rasa/cli/arguments/data.py +27 -2
- rasa/cli/arguments/default_arguments.py +25 -3
- rasa/cli/arguments/run.py +9 -9
- rasa/cli/arguments/train.py +11 -3
- rasa/cli/data.py +70 -8
- rasa/cli/e2e_test.py +104 -431
- rasa/cli/evaluate.py +1 -1
- rasa/cli/interactive.py +1 -0
- rasa/cli/llm_fine_tuning.py +398 -0
- rasa/cli/project_templates/calm/endpoints.yml +1 -1
- rasa/cli/project_templates/tutorial/endpoints.yml +1 -1
- rasa/cli/run.py +15 -14
- rasa/cli/scaffold.py +10 -8
- rasa/cli/studio/studio.py +35 -5
- rasa/cli/train.py +56 -8
- rasa/cli/utils.py +22 -5
- rasa/cli/x.py +1 -1
- rasa/constants.py +7 -1
- rasa/core/actions/action.py +98 -49
- rasa/core/actions/action_run_slot_rejections.py +4 -1
- rasa/core/actions/custom_action_executor.py +9 -6
- rasa/core/actions/direct_custom_actions_executor.py +80 -0
- rasa/core/actions/e2e_stub_custom_action_executor.py +68 -0
- rasa/core/actions/grpc_custom_action_executor.py +2 -2
- rasa/core/actions/http_custom_action_executor.py +6 -5
- rasa/core/agent.py +21 -17
- rasa/core/channels/__init__.py +2 -0
- rasa/core/channels/audiocodes.py +1 -16
- rasa/core/channels/voice_aware/__init__.py +0 -0
- rasa/core/channels/voice_aware/jambonz.py +103 -0
- rasa/core/channels/voice_aware/jambonz_protocol.py +344 -0
- rasa/core/channels/voice_aware/utils.py +20 -0
- rasa/core/channels/voice_native/__init__.py +0 -0
- rasa/core/constants.py +6 -1
- rasa/core/information_retrieval/faiss.py +7 -4
- rasa/core/information_retrieval/information_retrieval.py +8 -0
- rasa/core/information_retrieval/milvus.py +9 -2
- rasa/core/information_retrieval/qdrant.py +1 -1
- rasa/core/nlg/contextual_response_rephraser.py +32 -10
- rasa/core/nlg/summarize.py +4 -3
- rasa/core/policies/enterprise_search_policy.py +113 -45
- rasa/core/policies/flows/flow_executor.py +122 -76
- rasa/core/policies/intentless_policy.py +83 -29
- rasa/core/processor.py +72 -54
- rasa/core/run.py +5 -4
- rasa/core/tracker_store.py +8 -4
- rasa/core/training/interactive.py +1 -1
- rasa/core/utils.py +56 -57
- rasa/dialogue_understanding/coexistence/llm_based_router.py +53 -13
- rasa/dialogue_understanding/commands/__init__.py +6 -0
- rasa/dialogue_understanding/commands/restart_command.py +58 -0
- rasa/dialogue_understanding/commands/session_start_command.py +59 -0
- rasa/dialogue_understanding/commands/utils.py +40 -0
- rasa/dialogue_understanding/generator/constants.py +10 -3
- rasa/dialogue_understanding/generator/flow_retrieval.py +21 -5
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +13 -3
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +134 -90
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +47 -7
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +127 -41
- rasa/dialogue_understanding/patterns/restart.py +37 -0
- rasa/dialogue_understanding/patterns/session_start.py +37 -0
- rasa/dialogue_understanding/processor/command_processor.py +16 -3
- rasa/dialogue_understanding/processor/command_processor_component.py +6 -2
- rasa/e2e_test/aggregate_test_stats_calculator.py +134 -0
- rasa/e2e_test/assertions.py +1223 -0
- rasa/e2e_test/assertions_schema.yml +106 -0
- rasa/e2e_test/constants.py +20 -0
- rasa/e2e_test/e2e_config.py +220 -0
- rasa/e2e_test/e2e_config_schema.yml +26 -0
- rasa/e2e_test/e2e_test_case.py +131 -8
- rasa/e2e_test/e2e_test_converter.py +363 -0
- rasa/e2e_test/e2e_test_converter_prompt.jinja2 +70 -0
- rasa/e2e_test/e2e_test_coverage_report.py +364 -0
- rasa/e2e_test/e2e_test_result.py +26 -6
- rasa/e2e_test/e2e_test_runner.py +493 -71
- rasa/e2e_test/e2e_test_schema.yml +96 -0
- rasa/e2e_test/pykwalify_extensions.py +39 -0
- rasa/e2e_test/stub_custom_action.py +70 -0
- rasa/e2e_test/utils/__init__.py +0 -0
- rasa/e2e_test/utils/e2e_yaml_utils.py +55 -0
- rasa/e2e_test/utils/io.py +598 -0
- rasa/e2e_test/utils/validation.py +80 -0
- rasa/engine/graph.py +9 -3
- rasa/engine/recipes/default_components.py +0 -2
- rasa/engine/recipes/default_recipe.py +10 -2
- rasa/engine/storage/local_model_storage.py +40 -12
- rasa/engine/validation.py +78 -1
- rasa/env.py +9 -0
- rasa/graph_components/providers/story_graph_provider.py +59 -6
- rasa/llm_fine_tuning/__init__.py +0 -0
- rasa/llm_fine_tuning/annotation_module.py +241 -0
- rasa/llm_fine_tuning/conversations.py +144 -0
- rasa/llm_fine_tuning/llm_data_preparation_module.py +178 -0
- rasa/llm_fine_tuning/notebooks/unsloth_finetuning.ipynb +407 -0
- rasa/llm_fine_tuning/paraphrasing/__init__.py +0 -0
- rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +281 -0
- rasa/llm_fine_tuning/paraphrasing/default_rephrase_prompt_template.jina2 +44 -0
- rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +121 -0
- rasa/llm_fine_tuning/paraphrasing/rephrased_user_message.py +10 -0
- rasa/llm_fine_tuning/paraphrasing_module.py +128 -0
- rasa/llm_fine_tuning/storage.py +174 -0
- rasa/llm_fine_tuning/train_test_split_module.py +441 -0
- rasa/model_training.py +56 -16
- rasa/nlu/persistor.py +157 -36
- rasa/server.py +45 -10
- rasa/shared/constants.py +76 -16
- rasa/shared/core/domain.py +27 -19
- rasa/shared/core/events.py +28 -2
- rasa/shared/core/flows/flow.py +208 -13
- rasa/shared/core/flows/flow_path.py +84 -0
- rasa/shared/core/flows/flows_list.py +33 -11
- rasa/shared/core/flows/flows_yaml_schema.json +269 -193
- rasa/shared/core/flows/validation.py +112 -25
- rasa/shared/core/flows/yaml_flows_io.py +149 -10
- rasa/shared/core/trackers.py +6 -0
- rasa/shared/core/training_data/structures.py +20 -0
- rasa/shared/core/training_data/visualization.html +2 -2
- rasa/shared/exceptions.py +4 -0
- rasa/shared/importers/importer.py +64 -16
- rasa/shared/nlu/constants.py +2 -0
- rasa/shared/providers/_configs/__init__.py +0 -0
- rasa/shared/providers/_configs/azure_openai_client_config.py +183 -0
- rasa/shared/providers/_configs/client_config.py +57 -0
- rasa/shared/providers/_configs/default_litellm_client_config.py +130 -0
- rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +234 -0
- rasa/shared/providers/_configs/openai_client_config.py +175 -0
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +176 -0
- rasa/shared/providers/_configs/utils.py +101 -0
- rasa/shared/providers/_ssl_verification_utils.py +124 -0
- rasa/shared/providers/embedding/__init__.py +0 -0
- rasa/shared/providers/embedding/_base_litellm_embedding_client.py +259 -0
- rasa/shared/providers/embedding/_langchain_embedding_client_adapter.py +74 -0
- rasa/shared/providers/embedding/azure_openai_embedding_client.py +277 -0
- rasa/shared/providers/embedding/default_litellm_embedding_client.py +102 -0
- rasa/shared/providers/embedding/embedding_client.py +90 -0
- rasa/shared/providers/embedding/embedding_response.py +41 -0
- rasa/shared/providers/embedding/huggingface_local_embedding_client.py +191 -0
- rasa/shared/providers/embedding/openai_embedding_client.py +172 -0
- rasa/shared/providers/llm/__init__.py +0 -0
- rasa/shared/providers/llm/_base_litellm_client.py +251 -0
- rasa/shared/providers/llm/azure_openai_llm_client.py +338 -0
- rasa/shared/providers/llm/default_litellm_llm_client.py +84 -0
- rasa/shared/providers/llm/llm_client.py +76 -0
- rasa/shared/providers/llm/llm_response.py +50 -0
- rasa/shared/providers/llm/openai_llm_client.py +155 -0
- rasa/shared/providers/llm/self_hosted_llm_client.py +293 -0
- rasa/shared/providers/mappings.py +75 -0
- rasa/shared/utils/cli.py +30 -0
- rasa/shared/utils/io.py +65 -2
- rasa/shared/utils/llm.py +246 -200
- rasa/shared/utils/yaml.py +121 -15
- rasa/studio/auth.py +6 -4
- rasa/studio/config.py +13 -4
- rasa/studio/constants.py +1 -0
- rasa/studio/data_handler.py +10 -3
- rasa/studio/download.py +19 -13
- rasa/studio/train.py +2 -3
- rasa/studio/upload.py +19 -11
- rasa/telemetry.py +113 -58
- rasa/tracing/instrumentation/attribute_extractors.py +32 -17
- rasa/utils/common.py +18 -19
- rasa/utils/endpoints.py +7 -4
- rasa/utils/json_utils.py +60 -0
- rasa/utils/licensing.py +9 -1
- rasa/utils/ml_utils.py +4 -2
- rasa/validator.py +213 -3
- rasa/version.py +1 -1
- rasa_pro-3.10.16.dist-info/METADATA +196 -0
- {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/RECORD +179 -113
- rasa/nlu/classifiers/llm_intent_classifier.py +0 -519
- rasa/shared/providers/openai/clients.py +0 -43
- rasa/shared/providers/openai/session_handler.py +0 -110
- rasa_pro-3.9.18.dist-info/METADATA +0 -563
- /rasa/{shared/providers/openai → cli/project_templates/tutorial/actions}/__init__.py +0 -0
- /rasa/cli/project_templates/tutorial/{actions.py → actions/actions.py} +0 -0
- {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/NOTICE +0 -0
- {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/WHEEL +0 -0
- {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/entry_points.txt +0 -0
rasa/cli/e2e_test.py
CHANGED
|
@@ -1,50 +1,62 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import asyncio
|
|
3
|
-
import logging
|
|
4
|
-
import math
|
|
5
|
-
import shutil
|
|
6
3
|
import sys
|
|
7
4
|
from pathlib import Path
|
|
8
|
-
from
|
|
9
|
-
|
|
5
|
+
from typing import List
|
|
6
|
+
|
|
7
|
+
import structlog
|
|
10
8
|
|
|
11
9
|
import rasa.cli.arguments.run
|
|
12
10
|
import rasa.cli.utils
|
|
13
11
|
import rasa.shared.data
|
|
14
12
|
import rasa.shared.utils.cli
|
|
15
13
|
import rasa.shared.utils.io
|
|
16
|
-
import
|
|
14
|
+
import rasa.utils.io
|
|
17
15
|
from rasa.cli import SubParsersAction
|
|
18
|
-
from rasa.cli.arguments.default_arguments import
|
|
16
|
+
from rasa.cli.arguments.default_arguments import (
|
|
17
|
+
add_endpoint_param,
|
|
18
|
+
add_model_param,
|
|
19
|
+
add_remote_storage_param,
|
|
20
|
+
)
|
|
19
21
|
from rasa.core.exceptions import AgentNotReady
|
|
20
22
|
from rasa.core.utils import AvailableEndpoints
|
|
21
|
-
from rasa.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
from rasa.e2e_test.constants import
|
|
23
|
+
from rasa.e2e_test.aggregate_test_stats_calculator import (
|
|
24
|
+
AggregateTestStatsCalculator,
|
|
25
|
+
)
|
|
26
|
+
from rasa.e2e_test.constants import (
|
|
27
|
+
DEFAULT_COVERAGE_OUTPUT_PATH,
|
|
28
|
+
DEFAULT_E2E_INPUT_TESTS_PATH,
|
|
29
|
+
DEFAULT_E2E_OUTPUT_TESTS_PATH,
|
|
30
|
+
STATUS_FAILED,
|
|
31
|
+
STATUS_PASSED,
|
|
32
|
+
)
|
|
25
33
|
from rasa.e2e_test.e2e_test_case import (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
TestSuite,
|
|
34
|
+
KEY_STUB_CUSTOM_ACTIONS,
|
|
35
|
+
)
|
|
36
|
+
from rasa.e2e_test.e2e_test_coverage_report import (
|
|
37
|
+
create_coverage_report,
|
|
38
|
+
extract_tested_commands,
|
|
32
39
|
)
|
|
33
|
-
from rasa.e2e_test.e2e_test_result import TestResult
|
|
34
40
|
from rasa.e2e_test.e2e_test_runner import E2ETestRunner
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
from rasa.e2e_test.utils.io import (
|
|
42
|
+
_save_coverage_report,
|
|
43
|
+
_save_tested_commands_histogram,
|
|
44
|
+
extract_test_case_from_path,
|
|
45
|
+
print_test_result,
|
|
46
|
+
read_test_cases,
|
|
47
|
+
save_test_cases_to_yaml,
|
|
48
|
+
split_into_passed_failed,
|
|
49
|
+
write_test_results_to_file,
|
|
41
50
|
)
|
|
51
|
+
from rasa.e2e_test.utils.validation import validate_model_path
|
|
52
|
+
from rasa.exceptions import RasaException
|
|
53
|
+
from rasa.shared.constants import DEFAULT_ENDPOINTS_PATH, DEFAULT_MODELS_PATH
|
|
54
|
+
from rasa.utils.beta import ensure_beta_feature_is_enabled
|
|
55
|
+
from rasa.utils.endpoints import EndpointConfig
|
|
42
56
|
|
|
43
|
-
|
|
44
|
-
DEFAULT_E2E_OUTPUT_TESTS_PATH = "tests/e2e_results.yml"
|
|
45
|
-
KEY_TEST_CASES = "test_cases"
|
|
57
|
+
RASA_PRO_BETA_FINE_TUNING_RECIPE_ENV_VAR_NAME = "RASA_PRO_BETA_FINE_TUNING_RECIPE"
|
|
46
58
|
|
|
47
|
-
|
|
59
|
+
structlogger = structlog.get_logger()
|
|
48
60
|
|
|
49
61
|
|
|
50
62
|
def add_subparser(
|
|
@@ -121,154 +133,38 @@ def add_e2e_test_arguments(parser: argparse.ArgumentParser) -> None:
|
|
|
121
133
|
help="Results file containing end-to-end testing summary.",
|
|
122
134
|
)
|
|
123
135
|
|
|
136
|
+
add_remote_storage_param(parser)
|
|
137
|
+
|
|
124
138
|
parser.add_argument(
|
|
125
|
-
"--
|
|
126
|
-
|
|
139
|
+
"--coverage-report",
|
|
140
|
+
action="store_true",
|
|
141
|
+
help="Generate a coverage report on flow paths and commands covered in e2e "
|
|
142
|
+
"tests.",
|
|
127
143
|
)
|
|
128
144
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
"""Get the summary of the test results.
|
|
134
|
-
|
|
135
|
-
Args:
|
|
136
|
-
results: List of test results.
|
|
137
|
-
|
|
138
|
-
Returns:
|
|
139
|
-
Tuple consisting of passed count, failed count and failed test cases.
|
|
140
|
-
"""
|
|
141
|
-
passed_cases = [r for r in results if r.pass_status]
|
|
142
|
-
failed_cases = [r for r in results if not r.pass_status]
|
|
143
|
-
|
|
144
|
-
return passed_cases, failed_cases
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
def is_test_case_file(file_path: Union[Text, Path]) -> bool:
|
|
148
|
-
"""Check if file contains test cases.
|
|
149
|
-
|
|
150
|
-
Args:
|
|
151
|
-
file_path: Path of the file to check.
|
|
152
|
-
|
|
153
|
-
Returns:
|
|
154
|
-
`True` if the file contains test cases, `False` otherwise.
|
|
155
|
-
"""
|
|
156
|
-
return rasa.shared.data.is_likely_yaml_file(file_path) and is_key_in_yaml(
|
|
157
|
-
file_path, KEY_TEST_CASES
|
|
145
|
+
parser.add_argument(
|
|
146
|
+
"--coverage-output-path",
|
|
147
|
+
default=DEFAULT_COVERAGE_OUTPUT_PATH,
|
|
148
|
+
help="Directory where to save coverage report to.",
|
|
158
149
|
)
|
|
159
150
|
|
|
160
151
|
|
|
161
|
-
def validate_path_to_test_cases(path: Text) -> None:
|
|
162
|
-
"""Validate that path to test cases exists."""
|
|
163
|
-
if not Path(path).exists():
|
|
164
|
-
rasa.shared.utils.io.raise_warning(
|
|
165
|
-
f"Path to test cases does not exist: {path}. "
|
|
166
|
-
f"Please provide a valid path to test cases. "
|
|
167
|
-
f"Exiting...",
|
|
168
|
-
UserWarning,
|
|
169
|
-
)
|
|
170
|
-
sys.exit(1)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
def extract_test_case_from_path(path: Text) -> Tuple[Text, Text]:
|
|
174
|
-
"""Extract test case from path if specified.
|
|
175
|
-
|
|
176
|
-
Args:
|
|
177
|
-
path: Path to the file or folder containing test cases.
|
|
178
|
-
|
|
179
|
-
Returns:
|
|
180
|
-
Tuple consisting of the path to test cases and the extracted test case name.
|
|
181
|
-
"""
|
|
182
|
-
test_case_name = ""
|
|
183
|
-
|
|
184
|
-
if "::" in str(path):
|
|
185
|
-
splitted_path = path.split("::")
|
|
186
|
-
test_case_name = splitted_path[-1]
|
|
187
|
-
path = splitted_path[0]
|
|
188
|
-
|
|
189
|
-
return path, test_case_name
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
def validate_test_case(test_case_name: Text, input_test_cases: List[TestCase]) -> None:
|
|
193
|
-
"""Validate that test case exists."""
|
|
194
|
-
if test_case_name and not input_test_cases:
|
|
195
|
-
rasa.shared.utils.io.raise_warning(
|
|
196
|
-
f"Test case does not exist: {test_case_name}. "
|
|
197
|
-
f"Please check for typos and provide a valid test case name. "
|
|
198
|
-
f"Exiting...",
|
|
199
|
-
UserWarning,
|
|
200
|
-
)
|
|
201
|
-
sys.exit(1)
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
def read_test_cases(path: Text) -> TestSuite:
|
|
205
|
-
"""Read test cases from the given path.
|
|
206
|
-
|
|
207
|
-
Args:
|
|
208
|
-
path: Path to the file or folder containing test cases.
|
|
209
|
-
|
|
210
|
-
Returns:
|
|
211
|
-
TestSuite.
|
|
212
|
-
"""
|
|
213
|
-
path, test_case_name = extract_test_case_from_path(path)
|
|
214
|
-
validate_path_to_test_cases(path)
|
|
215
|
-
|
|
216
|
-
test_files = rasa.shared.data.get_data_files([path], is_test_case_file)
|
|
217
|
-
e2e_test_schema = read_e2e_test_schema()
|
|
218
|
-
|
|
219
|
-
input_test_cases = []
|
|
220
|
-
fixtures: Dict[Text, Fixture] = {}
|
|
221
|
-
metadata: Dict[Text, Metadata] = {}
|
|
222
|
-
|
|
223
|
-
for test_file in test_files:
|
|
224
|
-
test_file_content = parse_raw_yaml(Path(test_file).read_text())
|
|
225
|
-
validate_yaml_content_using_schema(test_file_content, e2e_test_schema)
|
|
226
|
-
|
|
227
|
-
test_cases_content = test_file_content.get(KEY_TEST_CASES) or []
|
|
228
|
-
|
|
229
|
-
if test_case_name:
|
|
230
|
-
test_cases = [
|
|
231
|
-
TestCase.from_dict(test_case_dict, file=test_file)
|
|
232
|
-
for test_case_dict in test_cases_content
|
|
233
|
-
if test_case_name == test_case_dict.get(KEY_TEST_CASE)
|
|
234
|
-
]
|
|
235
|
-
else:
|
|
236
|
-
test_cases = [
|
|
237
|
-
TestCase.from_dict(test_case_dict, file=test_file)
|
|
238
|
-
for test_case_dict in test_cases_content
|
|
239
|
-
]
|
|
240
|
-
|
|
241
|
-
input_test_cases.extend(test_cases)
|
|
242
|
-
fixtures_content = test_file_content.get(KEY_FIXTURES) or []
|
|
243
|
-
metadata_contents = test_file_content.get(KEY_METADATA) or []
|
|
244
|
-
for fixture in fixtures_content:
|
|
245
|
-
fixture_obj = Fixture.from_dict(fixture_dict=fixture)
|
|
246
|
-
|
|
247
|
-
# avoid adding duplicates from across multiple files
|
|
248
|
-
if fixtures.get(fixture_obj.name) is None:
|
|
249
|
-
fixtures[fixture_obj.name] = fixture_obj
|
|
250
|
-
|
|
251
|
-
for metadata_content in metadata_contents:
|
|
252
|
-
metadata_obj = Metadata.from_dict(metadata_dict=metadata_content)
|
|
253
|
-
|
|
254
|
-
# avoid adding duplicates from across multiple files
|
|
255
|
-
if metadata.get(metadata_obj.name) is None:
|
|
256
|
-
metadata[metadata_obj.name] = metadata_obj
|
|
257
|
-
|
|
258
|
-
validate_test_case(test_case_name, input_test_cases)
|
|
259
|
-
return TestSuite(input_test_cases, list(fixtures.values()), list(metadata.values()))
|
|
260
|
-
|
|
261
|
-
|
|
262
152
|
def execute_e2e_tests(args: argparse.Namespace) -> None:
|
|
263
153
|
"""Run the end-to-end tests.
|
|
264
154
|
|
|
265
155
|
Args:
|
|
266
156
|
args: Commandline arguments.
|
|
267
157
|
"""
|
|
158
|
+
if args.coverage_report:
|
|
159
|
+
ensure_beta_feature_is_enabled(
|
|
160
|
+
"LLM fine-tuning recipe",
|
|
161
|
+
env_flag=RASA_PRO_BETA_FINE_TUNING_RECIPE_ENV_VAR_NAME,
|
|
162
|
+
)
|
|
163
|
+
|
|
268
164
|
args.endpoints = rasa.cli.utils.get_validated_path(
|
|
269
165
|
args.endpoints, "endpoints", DEFAULT_ENDPOINTS_PATH, True
|
|
270
166
|
)
|
|
271
|
-
endpoints = AvailableEndpoints.
|
|
167
|
+
endpoints = AvailableEndpoints.get_instance(args.endpoints)
|
|
272
168
|
|
|
273
169
|
# Ignore all endpoints apart from action server, model, nlu and nlg
|
|
274
170
|
# to ensure InMemoryTrackerStore is being used instead of production
|
|
@@ -286,15 +182,28 @@ def execute_e2e_tests(args: argparse.Namespace) -> None:
|
|
|
286
182
|
|
|
287
183
|
test_suite = read_test_cases(path_to_test_cases)
|
|
288
184
|
|
|
185
|
+
if test_suite.stub_custom_actions:
|
|
186
|
+
if not endpoints.action:
|
|
187
|
+
endpoints.action = EndpointConfig()
|
|
188
|
+
|
|
189
|
+
endpoints.action.kwargs[KEY_STUB_CUSTOM_ACTIONS] = (
|
|
190
|
+
test_suite.stub_custom_actions
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
test_case_path, _ = extract_test_case_from_path(path_to_test_cases)
|
|
194
|
+
|
|
289
195
|
try:
|
|
290
196
|
test_runner = E2ETestRunner(
|
|
291
197
|
remote_storage=args.remote_storage,
|
|
292
198
|
model_path=args.model,
|
|
293
199
|
model_server=endpoints.model,
|
|
294
200
|
endpoints=endpoints,
|
|
201
|
+
test_case_path=Path(test_case_path),
|
|
295
202
|
)
|
|
296
203
|
except AgentNotReady as error:
|
|
297
|
-
|
|
204
|
+
structlogger.error(
|
|
205
|
+
"rasa.e2e_test.execute_e2e_tests.agent_not_ready", message=error.message
|
|
206
|
+
)
|
|
298
207
|
sys.exit(1)
|
|
299
208
|
|
|
300
209
|
results = asyncio.run(
|
|
@@ -303,284 +212,48 @@ def execute_e2e_tests(args: argparse.Namespace) -> None:
|
|
|
303
212
|
test_suite.fixtures,
|
|
304
213
|
args.fail_fast,
|
|
305
214
|
input_metadata=test_suite.metadata,
|
|
215
|
+
coverage=args.coverage_report,
|
|
306
216
|
)
|
|
307
217
|
)
|
|
308
218
|
|
|
309
|
-
if args.e2e_results is not None:
|
|
310
|
-
write_test_results_to_file(results, args.e2e_results)
|
|
311
|
-
|
|
312
219
|
passed, failed = split_into_passed_failed(results)
|
|
313
|
-
print_test_result(passed, failed, args.fail_fast)
|
|
314
|
-
|
|
315
220
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
Args:
|
|
320
|
-
results: List of test results.
|
|
321
|
-
output_file: Path to the output file.
|
|
322
|
-
"""
|
|
323
|
-
Path(output_file).touch()
|
|
324
|
-
|
|
325
|
-
data = {"test_results": [test_result.as_dict() for test_result in results]}
|
|
326
|
-
|
|
327
|
-
rasa.utils.io.write_yaml(
|
|
328
|
-
data, target=output_file, transform=transform_results_output_to_yaml
|
|
329
|
-
)
|
|
330
|
-
|
|
331
|
-
rasa.shared.utils.cli.print_info(
|
|
332
|
-
f"Overall results have been saved at path: {output_file}."
|
|
333
|
-
)
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
def transform_results_output_to_yaml(yaml_string: Text) -> Text:
|
|
337
|
-
"""Transform the output of the YAML writer to make it more readable.
|
|
338
|
-
|
|
339
|
-
Args:
|
|
340
|
-
yaml_string: The YAML string to transform.
|
|
341
|
-
|
|
342
|
-
Returns:
|
|
343
|
-
The transformed YAML string.
|
|
344
|
-
"""
|
|
345
|
-
result = []
|
|
346
|
-
for s in yaml_string.splitlines(True):
|
|
347
|
-
if s.startswith("- name"):
|
|
348
|
-
result.append("\n")
|
|
349
|
-
result.append(s)
|
|
350
|
-
elif s.startswith("\n"):
|
|
351
|
-
result.append(s.strip())
|
|
352
|
-
else:
|
|
353
|
-
result.append(s)
|
|
354
|
-
return "".join(result)
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
def pad(text: Text, char: Text = "=", min: int = 3) -> Text:
|
|
358
|
-
"""Pad text to a certain length.
|
|
359
|
-
|
|
360
|
-
Uses `char` to pad the text to the specified length. If the text is longer
|
|
361
|
-
than the specified length, at least `min` are used.
|
|
362
|
-
|
|
363
|
-
The padding is applied to the left and right of the text (almost) equally.
|
|
364
|
-
|
|
365
|
-
Example:
|
|
366
|
-
>>> pad("Hello")
|
|
367
|
-
"========= Hello ========"
|
|
368
|
-
>>> pad("Hello", char="-")
|
|
369
|
-
"--------- Hello --------"
|
|
370
|
-
|
|
371
|
-
Args:
|
|
372
|
-
text: Text to pad.
|
|
373
|
-
min: Minimum length of the padding.
|
|
374
|
-
char: Character to pad with.
|
|
375
|
-
|
|
376
|
-
Returns:
|
|
377
|
-
Padded text.
|
|
378
|
-
"""
|
|
379
|
-
width = shutil.get_terminal_size((80, 20)).columns
|
|
380
|
-
padding = max(width - len(text) - 2, min * 2)
|
|
381
|
-
|
|
382
|
-
return char * (padding // 2) + " " + text + " " + char * math.ceil(padding / 2)
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
def color_difference(diff: List[Text]) -> Generator[Text, None, None]:
|
|
386
|
-
"""Colorize the difference between two strings.
|
|
387
|
-
|
|
388
|
-
Example:
|
|
389
|
-
>>> color_difference(["+ Hello", "- World"])
|
|
390
|
-
["<ansigreen>+ Hello</ansigreen>", "<ansired>- World</ansired>"]
|
|
391
|
-
|
|
392
|
-
Args:
|
|
393
|
-
diff: List of lines of the diff.
|
|
394
|
-
|
|
395
|
-
Returns:
|
|
396
|
-
Generator of colored lines.
|
|
397
|
-
"""
|
|
398
|
-
for line in diff:
|
|
399
|
-
if line.startswith("+"):
|
|
400
|
-
yield "[green3]" + line + "[/green3]"
|
|
401
|
-
elif line.startswith("-"):
|
|
402
|
-
yield "[red3]" + line + "[/red3]"
|
|
403
|
-
elif line.startswith("^"):
|
|
404
|
-
yield "[blue3]" + line + "[/blue3]"
|
|
405
|
-
elif line.startswith("?"):
|
|
406
|
-
yield "[grey37]" + line + "[/grey37]"
|
|
407
|
-
else:
|
|
408
|
-
yield line
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
def print_failed_case(fail: TestResult) -> None:
|
|
412
|
-
"""Print the details of a failed test case.
|
|
413
|
-
|
|
414
|
-
Example:
|
|
415
|
-
>>> print_failed_case(TestResult(TestCase("test", "test.md"), 1,
|
|
416
|
-
... ["- Hello", "+ World"]))
|
|
417
|
-
---------------------- test in test.md failed ----------------------
|
|
418
|
-
Mismatch starting at test.md:1:
|
|
419
|
-
<ansired>- Hello</ansired>
|
|
420
|
-
<ansigreen>+ World</ansigreen>
|
|
421
|
-
"""
|
|
422
|
-
fail_headline = (
|
|
423
|
-
f"'{fail.test_case.name}' in {fail.test_case.file_with_line()} failed"
|
|
424
|
-
)
|
|
425
|
-
rasa.shared.utils.cli.print_error(f"{pad(fail_headline, char='-')}\n")
|
|
426
|
-
print(f"Mismatch starting at {fail.test_case.file}:{fail.error_line}: \n")
|
|
427
|
-
rich.print(("\n".join(color_difference(fail.difference))))
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
def print_test_summary(failed: List[TestResult]) -> None:
|
|
431
|
-
"""Print the summary of the test run.
|
|
432
|
-
|
|
433
|
-
Example:
|
|
434
|
-
>>> print_test_summary([TestResult(TestCase("test", "test.md"), 1,
|
|
435
|
-
... ["- Hello", "+ World"])])
|
|
436
|
-
=================== short test summary info ===================
|
|
437
|
-
FAILED test.md::test
|
|
438
|
-
"""
|
|
439
|
-
rasa.shared.utils.cli.print_info(pad("short test summary info"))
|
|
440
|
-
|
|
441
|
-
for f in failed:
|
|
442
|
-
rasa.shared.utils.cli.print_error(
|
|
443
|
-
f"FAILED {f.test_case.file}::{f.test_case.name}"
|
|
444
|
-
)
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
def print_final_line(
|
|
448
|
-
passed: List[TestResult], failed: List[TestResult], has_failed: bool
|
|
449
|
-
) -> None:
|
|
450
|
-
"""Print the final line of the test output.
|
|
451
|
-
|
|
452
|
-
Args:
|
|
453
|
-
passed: List of passed test cases.
|
|
454
|
-
failed: List of failed test cases.
|
|
455
|
-
has_failed: Boolean, true if the test run has failed.
|
|
456
|
-
"""
|
|
457
|
-
final_line_color = "green3" if not has_failed else "red3"
|
|
458
|
-
|
|
459
|
-
width = shutil.get_terminal_size((80, 20)).columns
|
|
460
|
-
|
|
461
|
-
# calculate the length of the text - this is a bit hacky but works
|
|
462
|
-
text_lengt = (
|
|
463
|
-
math.ceil(len(passed) / 10) # length of the number of passed tests
|
|
464
|
-
+ math.ceil(len(failed) / 10) # length of the number of failed tests
|
|
465
|
-
+ 18 # length of the text " failed, passed "
|
|
466
|
-
)
|
|
467
|
-
# we can't use the padding function here as the text contains html tags
|
|
468
|
-
# which are not taken into account when calculating the length
|
|
469
|
-
padding = max(6, width - text_lengt)
|
|
470
|
-
pre_pad = "=" * max(3, padding // 2)
|
|
471
|
-
post_pad = "=" * max(3, math.ceil(padding / 2))
|
|
472
|
-
rich.print(
|
|
473
|
-
f"[{final_line_color}]{pre_pad} "
|
|
474
|
-
f"[bold red3]{len(failed)} failed[/bold red3]"
|
|
475
|
-
f"[bright_white], [/bright_white]"
|
|
476
|
-
f"[bold green3]{len(passed)} passed[/bold green3]"
|
|
477
|
-
f" {post_pad}[/{final_line_color}]"
|
|
478
|
-
)
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
def print_test_result(
|
|
482
|
-
passed: List[TestResult],
|
|
483
|
-
failed: List[TestResult],
|
|
484
|
-
fail_fast: bool = False,
|
|
485
|
-
) -> None:
|
|
486
|
-
"""Print the result of the test run.
|
|
221
|
+
if args.e2e_results is not None:
|
|
222
|
+
results_path = Path(args.e2e_results)
|
|
487
223
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
failed: List of failed test cases.
|
|
491
|
-
fail_fast: If true, stop after the first failure.
|
|
492
|
-
"""
|
|
493
|
-
if failed:
|
|
494
|
-
# print failure headline
|
|
495
|
-
print("\n")
|
|
496
|
-
rich.print(f"[bold]{pad('FAILURES', char='=')}[/bold]")
|
|
497
|
-
|
|
498
|
-
# print failed test_Case
|
|
499
|
-
for fail in failed:
|
|
500
|
-
print_failed_case(fail)
|
|
501
|
-
|
|
502
|
-
print_test_summary(failed)
|
|
503
|
-
|
|
504
|
-
if fail_fast:
|
|
505
|
-
rasa.shared.utils.cli.print_error(pad("stopping after 1 failure", char="!"))
|
|
506
|
-
has_failed = True
|
|
507
|
-
elif len(failed) + len(passed) == 0:
|
|
508
|
-
# no tests were run, print error
|
|
509
|
-
rasa.shared.utils.cli.print_error(pad("no test cases found", char="!"))
|
|
510
|
-
print_e2e_help()
|
|
511
|
-
has_failed = True
|
|
512
|
-
elif failed:
|
|
513
|
-
has_failed = True
|
|
514
|
-
else:
|
|
515
|
-
has_failed = False
|
|
516
|
-
|
|
517
|
-
print_final_line(passed, failed, has_failed=has_failed)
|
|
518
|
-
sys.exit(1 if has_failed else 0)
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
def print_e2e_help() -> None:
|
|
522
|
-
"""Print help guiding users how to write e2e tests."""
|
|
523
|
-
rasa.shared.utils.cli.print_info(
|
|
524
|
-
dedent(
|
|
525
|
-
"""\
|
|
526
|
-
To start using e2e tests create a yaml file in a test directory, e.g.
|
|
527
|
-
'tests/test_cases.yml'. You can find example test cases in the starter
|
|
528
|
-
pack at
|
|
529
|
-
|
|
530
|
-
https://github.com/RasaHQ/starter-pack-intentless-policy#testing-the-policy
|
|
531
|
-
|
|
532
|
-
Here is an example of a test case in yaml format:
|
|
533
|
-
|
|
534
|
-
test_cases:
|
|
535
|
-
- test_case: "test_greet"
|
|
536
|
-
steps:
|
|
537
|
-
- user: "hello there!"
|
|
538
|
-
- bot: "Hey! How are you?"
|
|
539
|
-
|
|
540
|
-
To run the e2e tests, execute:
|
|
541
|
-
>>> rasa test e2e <path-to-test-cases>
|
|
542
|
-
"""
|
|
224
|
+
passed_file = rasa.cli.utils.get_e2e_results_file_name(
|
|
225
|
+
results_path, STATUS_PASSED
|
|
543
226
|
)
|
|
544
|
-
|
|
545
|
-
|
|
227
|
+
write_test_results_to_file(passed, passed_file)
|
|
546
228
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
) -> Text:
|
|
550
|
-
"""Validate the model path.
|
|
551
|
-
|
|
552
|
-
Args:
|
|
553
|
-
model_path: Path to the model.
|
|
554
|
-
parameter: Name of the parameter.
|
|
555
|
-
default: Default path to the model.
|
|
556
|
-
|
|
557
|
-
Returns:
|
|
558
|
-
Path to the model.
|
|
559
|
-
"""
|
|
560
|
-
if model_path and Path(model_path).exists():
|
|
561
|
-
return model_path
|
|
562
|
-
|
|
563
|
-
if model_path and not Path(model_path).exists():
|
|
564
|
-
rasa.shared.utils.io.raise_warning(
|
|
565
|
-
f"The provided model path '{model_path}' could not be found. "
|
|
566
|
-
f"Using default location '{default}' instead.",
|
|
567
|
-
UserWarning,
|
|
229
|
+
failed_file = rasa.cli.utils.get_e2e_results_file_name(
|
|
230
|
+
results_path, STATUS_FAILED
|
|
568
231
|
)
|
|
232
|
+
write_test_results_to_file(failed, failed_file)
|
|
569
233
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
234
|
+
aggregate_stats_calculator = AggregateTestStatsCalculator(
|
|
235
|
+
passed_results=passed, failed_results=failed, test_cases=test_suite.test_cases
|
|
236
|
+
)
|
|
237
|
+
accuracy_calculations = aggregate_stats_calculator.calculate()
|
|
238
|
+
|
|
239
|
+
if args.coverage_report and test_runner.agent.processor:
|
|
240
|
+
coverage_output_path = args.coverage_output_path
|
|
241
|
+
rasa.shared.utils.io.create_directory(coverage_output_path)
|
|
242
|
+
flows = asyncio.run(test_runner.agent.processor.get_flows())
|
|
243
|
+
|
|
244
|
+
for results, status in [(passed, STATUS_PASSED), (failed, STATUS_FAILED)]:
|
|
245
|
+
report = create_coverage_report(flows, results)
|
|
246
|
+
_save_coverage_report(report, status, coverage_output_path)
|
|
247
|
+
tested_commands = extract_tested_commands(results)
|
|
248
|
+
_save_tested_commands_histogram(
|
|
249
|
+
tested_commands, status, coverage_output_path
|
|
250
|
+
)
|
|
251
|
+
save_test_cases_to_yaml(results, coverage_output_path, status, test_suite)
|
|
252
|
+
|
|
253
|
+
rasa.shared.utils.cli.print_info(
|
|
254
|
+
f"Coverage data and result is written to {coverage_output_path}."
|
|
574
255
|
)
|
|
575
256
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
def read_e2e_test_schema() -> Union[List[Any], Dict[Text, Any]]:
|
|
581
|
-
"""Read the schema for the e2e test files.
|
|
582
|
-
|
|
583
|
-
Returns:
|
|
584
|
-
The content of the schema.
|
|
585
|
-
"""
|
|
586
|
-
return read_schema_file(SCHEMA_FILE_PATH)
|
|
257
|
+
print_test_result(
|
|
258
|
+
passed, failed, args.fail_fast, accuracy_calculations=accuracy_calculations
|
|
259
|
+
)
|
rasa/cli/evaluate.py
CHANGED
|
@@ -217,6 +217,6 @@ def _create_tracker_loader(
|
|
|
217
217
|
A MarkerTrackerLoader object configured with the specified strategy against
|
|
218
218
|
the configured tracker store.
|
|
219
219
|
"""
|
|
220
|
-
endpoints = AvailableEndpoints.
|
|
220
|
+
endpoints = AvailableEndpoints.get_instance(endpoint_config)
|
|
221
221
|
tracker_store = TrackerStore.create(endpoints.tracker_store, domain=domain)
|
|
222
222
|
return MarkerTrackerLoader(tracker_store, strategy, count, seed)
|
rasa/cli/interactive.py
CHANGED
|
@@ -108,6 +108,7 @@ def _set_not_required_args(args: argparse.Namespace) -> None:
|
|
|
108
108
|
args.skip_validation = True
|
|
109
109
|
args.fail_on_validation_warnings = False
|
|
110
110
|
args.validation_max_history = None
|
|
111
|
+
args.remote_storage = None
|
|
111
112
|
|
|
112
113
|
|
|
113
114
|
def perform_interactive_learning(
|