rasa-pro 3.10.16__py3-none-any.whl → 3.11.0a1__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 +396 -17
- rasa/api.py +9 -3
- rasa/cli/arguments/default_arguments.py +23 -2
- rasa/cli/arguments/run.py +15 -0
- rasa/cli/arguments/train.py +3 -9
- rasa/cli/e2e_test.py +1 -1
- rasa/cli/evaluate.py +1 -1
- rasa/cli/inspect.py +8 -4
- rasa/cli/llm_fine_tuning.py +12 -15
- rasa/cli/run.py +8 -1
- rasa/cli/studio/studio.py +8 -18
- rasa/cli/train.py +11 -53
- rasa/cli/utils.py +8 -10
- rasa/cli/x.py +1 -1
- rasa/constants.py +1 -1
- rasa/core/actions/action.py +2 -0
- rasa/core/actions/action_hangup.py +29 -0
- rasa/core/agent.py +2 -2
- rasa/core/brokers/kafka.py +3 -1
- rasa/core/brokers/pika.py +3 -1
- rasa/core/channels/__init__.py +8 -6
- rasa/core/channels/channel.py +21 -4
- rasa/core/channels/development_inspector.py +143 -46
- rasa/core/channels/inspector/README.md +1 -1
- rasa/core/channels/inspector/dist/assets/{arc-b6e548fe.js → arc-86942a71.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-d0fbc5ce-fa03ac9e.js → c4Diagram-d0fbc5ce-b0290676.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-936ed81e-ee67392a.js → classDiagram-936ed81e-f6405f6e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-c3cb15f1-9b283fae.js → classDiagram-v2-c3cb15f1-ef61ac77.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{createText-62fc7601-8b6fcc2a.js → createText-62fc7601-f0411e58.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-f2ad444c-22e77f4f.js → edges-f2ad444c-7dcc4f3b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9d236eb7-60ffc87f.js → erDiagram-9d236eb7-e0c092d7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-1972c806-9dd802e4.js → flowDb-1972c806-fba2e3ce.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-7ea5b25a-5fa1912f.js → flowDiagram-7ea5b25a-7a70b71a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-24a5f41a.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-abe16c3d-622a1fd2.js → flowchart-elk-definition-abe16c3d-00a59b68.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-9b5ea136-e285a63a.js → ganttDiagram-9b5ea136-293c91fa.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-99d0ae7c-f237bdca.js → gitGraphDiagram-99d0ae7c-07b2d68c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-2c4b9a3b-4b03d70e.js → index-2c4b9a3b-bc959fbd.js} +1 -1
- rasa/core/channels/inspector/dist/assets/index-3a8a5a28.js +1317 -0
- rasa/core/channels/inspector/dist/assets/{infoDiagram-736b4530-72a0fa5f.js → infoDiagram-736b4530-4a350f72.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-df861f2b-82218c41.js → journeyDiagram-df861f2b-af464fb7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-78cff630.js → layout-0071f036.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-5038b469.js → line-2f73cc83.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-c4fc4098.js → linear-f014b4cc.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-beec6740-c33c8ea6.js → mindmap-definition-beec6740-d2426fb6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-dbbf0591-a8d03059.js → pieDiagram-dbbf0591-776f01a2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-4d7f4fd6-6a0e56b2.js → quadrantDiagram-4d7f4fd6-82e00b57.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-6fc4c22a-2dc7c7bd.js → requirementDiagram-6fc4c22a-ea13c6bb.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-8f13d901-2360fe39.js → sankeyDiagram-8f13d901-1feca7e9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-b655622a-41b9f9ad.js → sequenceDiagram-b655622a-070c61d2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-59f0c015-0aad326f.js → stateDiagram-59f0c015-24f46263.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-2b26beab-9847d984.js → stateDiagram-v2-2b26beab-c9056051.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-080da4f6-564d890e.js → styles-080da4f6-08abc34a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-3dcbcfbf-38957613.js → styles-3dcbcfbf-bc74c25a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9c745c82-f0fc6921.js → styles-9c745c82-4e5d66de.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-4835440b-ef3c5a77.js → svgDrawCommon-4835440b-849c4517.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-5b62e21b-bf3e91c1.js → timeline-definition-5b62e21b-d0fb1598.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-2b33534f-4d4026c0.js → xychartDiagram-2b33534f-04d115e2.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +18 -17
- rasa/core/channels/inspector/index.html +17 -16
- rasa/core/channels/inspector/package.json +5 -1
- rasa/core/channels/inspector/src/App.tsx +117 -67
- rasa/core/channels/inspector/src/components/Chat.tsx +95 -0
- rasa/core/channels/inspector/src/components/DiagramFlow.tsx +11 -10
- rasa/core/channels/inspector/src/components/DialogueStack.tsx +10 -25
- rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +1 -1
- rasa/core/channels/inspector/src/helpers/formatters.test.ts +10 -0
- rasa/core/channels/inspector/src/helpers/formatters.ts +107 -41
- rasa/core/channels/inspector/src/helpers/utils.ts +92 -7
- rasa/core/channels/inspector/src/types.ts +21 -1
- rasa/core/channels/inspector/yarn.lock +94 -1
- rasa/core/channels/rest.py +51 -46
- rasa/core/channels/socketio.py +22 -0
- rasa/core/channels/{audiocodes.py → voice_ready/audiocodes.py} +110 -68
- rasa/core/channels/{voice_aware → voice_ready}/jambonz.py +11 -4
- rasa/core/channels/{voice_aware → voice_ready}/jambonz_protocol.py +57 -5
- rasa/core/channels/{twilio_voice.py → voice_ready/twilio_voice.py} +58 -7
- rasa/core/channels/{voice_aware → voice_ready}/utils.py +16 -0
- rasa/core/channels/voice_stream/asr/__init__.py +0 -0
- rasa/core/channels/voice_stream/asr/asr_engine.py +71 -0
- rasa/core/channels/voice_stream/asr/asr_event.py +13 -0
- rasa/core/channels/voice_stream/asr/deepgram.py +77 -0
- rasa/core/channels/voice_stream/audio_bytes.py +7 -0
- rasa/core/channels/voice_stream/tts/__init__.py +0 -0
- rasa/core/channels/voice_stream/tts/azure.py +100 -0
- rasa/core/channels/voice_stream/tts/cartesia.py +114 -0
- rasa/core/channels/voice_stream/tts/tts_cache.py +27 -0
- rasa/core/channels/voice_stream/tts/tts_engine.py +48 -0
- rasa/core/channels/voice_stream/twilio_media_streams.py +164 -0
- rasa/core/channels/voice_stream/util.py +57 -0
- rasa/core/channels/voice_stream/voice_channel.py +247 -0
- rasa/core/featurizers/single_state_featurizer.py +1 -22
- rasa/core/featurizers/tracker_featurizers.py +18 -115
- rasa/core/nlg/contextual_response_rephraser.py +11 -2
- rasa/{nlu → core}/persistor.py +16 -38
- rasa/core/policies/enterprise_search_policy.py +12 -15
- rasa/core/policies/flows/flow_executor.py +8 -18
- rasa/core/policies/intentless_policy.py +10 -15
- rasa/core/policies/ted_policy.py +33 -58
- rasa/core/policies/unexpected_intent_policy.py +7 -15
- rasa/core/processor.py +13 -64
- rasa/core/run.py +11 -1
- rasa/core/secrets_manager/constants.py +4 -0
- rasa/core/secrets_manager/factory.py +8 -0
- rasa/core/secrets_manager/vault.py +11 -1
- rasa/core/training/interactive.py +1 -1
- rasa/core/utils.py +1 -11
- rasa/dialogue_understanding/coexistence/llm_based_router.py +10 -10
- rasa/dialogue_understanding/commands/__init__.py +2 -0
- rasa/dialogue_understanding/commands/change_flow_command.py +0 -6
- rasa/dialogue_understanding/commands/session_end_command.py +61 -0
- rasa/dialogue_understanding/generator/flow_retrieval.py +0 -7
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +12 -3
- rasa/dialogue_understanding/generator/llm_command_generator.py +1 -1
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +3 -28
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +1 -19
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +4 -37
- rasa/e2e_test/aggregate_test_stats_calculator.py +1 -11
- rasa/e2e_test/assertions.py +6 -48
- rasa/e2e_test/e2e_test_runner.py +6 -9
- rasa/e2e_test/utils/e2e_yaml_utils.py +1 -1
- rasa/e2e_test/utils/io.py +1 -3
- rasa/engine/graph.py +3 -10
- rasa/engine/recipes/config_files/default_config.yml +0 -3
- rasa/engine/recipes/default_recipe.py +0 -1
- rasa/engine/recipes/graph_recipe.py +0 -1
- rasa/engine/runner/dask.py +2 -2
- rasa/engine/storage/local_model_storage.py +12 -42
- rasa/engine/storage/storage.py +1 -5
- rasa/engine/validation.py +1 -78
- rasa/keys +1 -0
- rasa/model_training.py +13 -16
- rasa/nlu/classifiers/diet_classifier.py +25 -38
- rasa/nlu/classifiers/logistic_regression_classifier.py +9 -22
- rasa/nlu/classifiers/sklearn_intent_classifier.py +16 -37
- rasa/nlu/extractors/crf_entity_extractor.py +50 -93
- rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +16 -45
- rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +17 -52
- rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +3 -5
- rasa/server.py +1 -1
- rasa/shared/constants.py +3 -12
- rasa/shared/core/constants.py +4 -0
- rasa/shared/core/domain.py +101 -47
- rasa/shared/core/events.py +29 -0
- rasa/shared/core/flows/flows_list.py +20 -11
- rasa/shared/core/flows/validation.py +25 -0
- rasa/shared/core/flows/yaml_flows_io.py +3 -24
- rasa/shared/importers/importer.py +40 -39
- rasa/shared/importers/multi_project.py +23 -11
- rasa/shared/importers/rasa.py +7 -2
- rasa/shared/importers/remote_importer.py +196 -0
- rasa/shared/importers/utils.py +3 -1
- rasa/shared/nlu/training_data/features.py +2 -120
- rasa/shared/nlu/training_data/training_data.py +18 -19
- rasa/shared/providers/_configs/azure_openai_client_config.py +3 -5
- rasa/shared/providers/embedding/_base_litellm_embedding_client.py +1 -6
- rasa/shared/providers/llm/_base_litellm_client.py +11 -31
- rasa/shared/providers/llm/self_hosted_llm_client.py +3 -15
- rasa/shared/utils/common.py +3 -22
- rasa/shared/utils/io.py +0 -1
- rasa/shared/utils/llm.py +30 -27
- rasa/shared/utils/schemas/events.py +2 -0
- rasa/shared/utils/schemas/model_config.yml +0 -10
- rasa/shared/utils/yaml.py +44 -0
- rasa/studio/auth.py +5 -3
- rasa/studio/config.py +4 -13
- rasa/studio/constants.py +0 -1
- rasa/studio/data_handler.py +3 -10
- rasa/studio/upload.py +8 -17
- rasa/tracing/instrumentation/attribute_extractors.py +1 -1
- rasa/utils/io.py +66 -0
- rasa/utils/tensorflow/model_data.py +193 -2
- rasa/validator.py +0 -12
- rasa/version.py +1 -1
- rasa_pro-3.11.0a1.dist-info/METADATA +576 -0
- {rasa_pro-3.10.16.dist-info → rasa_pro-3.11.0a1.dist-info}/RECORD +181 -164
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-1844e5a5.js +0 -1
- rasa/core/channels/inspector/dist/assets/index-a5d3e69d.js +0 -1040
- rasa/utils/tensorflow/feature_array.py +0 -366
- rasa_pro-3.10.16.dist-info/METADATA +0 -196
- /rasa/core/channels/{voice_aware → voice_ready}/__init__.py +0 -0
- /rasa/core/channels/{voice_native → voice_stream}/__init__.py +0 -0
- {rasa_pro-3.10.16.dist-info → rasa_pro-3.11.0a1.dist-info}/NOTICE +0 -0
- {rasa_pro-3.10.16.dist-info → rasa_pro-3.11.0a1.dist-info}/WHEEL +0 -0
- {rasa_pro-3.10.16.dist-info → rasa_pro-3.11.0a1.dist-info}/entry_points.txt +0 -0
rasa/shared/utils/yaml.py
CHANGED
|
@@ -416,6 +416,47 @@ def validate_raw_yaml_using_schema_file_with_responses(
|
|
|
416
416
|
)
|
|
417
417
|
|
|
418
418
|
|
|
419
|
+
def process_content(content: str) -> str:
|
|
420
|
+
"""
|
|
421
|
+
Process the content to handle both Windows paths and emojis.
|
|
422
|
+
Windows paths are processed by escaping backslashes but emojis are left untouched.
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
content: yaml content to be processed
|
|
426
|
+
"""
|
|
427
|
+
# Detect common Windows path patterns: e.g., C:\ or \\
|
|
428
|
+
UNESCAPED_WINDOWS_PATH_PATTERN = re.compile(
|
|
429
|
+
r"(?<!\w)[a-zA-Z]:(\\[a-zA-Z0-9_ -]+)*(\\)?(?!\\n)"
|
|
430
|
+
)
|
|
431
|
+
ESCAPED_WINDOWS_PATH_PATTERN = re.compile(
|
|
432
|
+
r"(?<!\w)[a-zA-Z]:(\\\\[a-zA-Z0-9_ -]+)+\\\\?(?!\\n)"
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
# Function to escape backslashes in Windows paths but leave other content as is
|
|
436
|
+
def escape_windows_paths(match: re.Match) -> str:
|
|
437
|
+
path = str(match.group(0))
|
|
438
|
+
return path.replace("\\", "\\\\") # Escape backslashes only in Windows paths
|
|
439
|
+
|
|
440
|
+
def unescape_windows_paths(match: re.Match) -> str:
|
|
441
|
+
path = str(match.group(0))
|
|
442
|
+
return path.replace("\\\\", "\\")
|
|
443
|
+
|
|
444
|
+
# First, process Windows paths by escaping backslashes
|
|
445
|
+
content = re.sub(UNESCAPED_WINDOWS_PATH_PATTERN, escape_windows_paths, content)
|
|
446
|
+
|
|
447
|
+
# Ensure proper handling of emojis by decoding Unicode sequences
|
|
448
|
+
content = (
|
|
449
|
+
content.encode("utf-8")
|
|
450
|
+
.decode("raw_unicode_escape")
|
|
451
|
+
.encode("utf-16", "surrogatepass")
|
|
452
|
+
.decode("utf-16")
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
content = re.sub(ESCAPED_WINDOWS_PATH_PATTERN, unescape_windows_paths, content)
|
|
456
|
+
|
|
457
|
+
return content
|
|
458
|
+
|
|
459
|
+
|
|
419
460
|
def read_yaml(
|
|
420
461
|
content: str,
|
|
421
462
|
reader_type: Union[str, List[str]] = "safe",
|
|
@@ -431,6 +472,9 @@ def read_yaml(
|
|
|
431
472
|
Raises:
|
|
432
473
|
ruamel.yaml.parser.ParserError: If there was an error when parsing the YAML.
|
|
433
474
|
"""
|
|
475
|
+
if _is_ascii(content):
|
|
476
|
+
content = process_content(content)
|
|
477
|
+
|
|
434
478
|
custom_constructor = kwargs.get("custom_constructor", None)
|
|
435
479
|
|
|
436
480
|
# Create YAML parser with custom constructor
|
rasa/studio/auth.py
CHANGED
|
@@ -23,10 +23,12 @@ from rasa.studio.results_logger import with_studio_error_handler, StudioResult
|
|
|
23
23
|
class StudioAuth:
|
|
24
24
|
"""Handles the authentication with the Rasa Studio authentication server."""
|
|
25
25
|
|
|
26
|
-
def __init__(
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
studio_config: StudioConfig,
|
|
29
|
+
verify: bool = True,
|
|
30
|
+
) -> None:
|
|
27
31
|
self.config = studio_config
|
|
28
|
-
verify = not studio_config.disable_verify
|
|
29
|
-
|
|
30
32
|
self.keycloak_openid = KeycloakOpenID(
|
|
31
33
|
server_url=studio_config.authentication_server_url,
|
|
32
34
|
client_id=studio_config.client_id,
|
rasa/studio/config.py
CHANGED
|
@@ -2,14 +2,13 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
from dataclasses import dataclass
|
|
5
|
-
from typing import
|
|
5
|
+
from typing import Dict, Optional, Text
|
|
6
6
|
|
|
7
7
|
from rasa.utils.common import read_global_config_value, write_global_config_value
|
|
8
8
|
|
|
9
9
|
from rasa.studio.constants import (
|
|
10
10
|
RASA_STUDIO_AUTH_SERVER_URL_ENV,
|
|
11
11
|
RASA_STUDIO_CLI_CLIENT_ID_KEY_ENV,
|
|
12
|
-
RASA_STUDIO_CLI_DISABLE_VERIFY_KEY_ENV,
|
|
13
12
|
RASA_STUDIO_CLI_REALM_NAME_KEY_ENV,
|
|
14
13
|
RASA_STUDIO_CLI_STUDIO_URL_ENV,
|
|
15
14
|
STUDIO_CONFIG_KEY,
|
|
@@ -20,7 +19,6 @@ STUDIO_URL_KEY = "studio_url"
|
|
|
20
19
|
CLIENT_ID_KEY = "client_id"
|
|
21
20
|
REALM_NAME_KEY = "realm_name"
|
|
22
21
|
CLIENT_SECRET_KEY = "client_secret"
|
|
23
|
-
DISABLE_VERIFY = "disable_verify"
|
|
24
22
|
|
|
25
23
|
|
|
26
24
|
@dataclass
|
|
@@ -29,15 +27,13 @@ class StudioConfig:
|
|
|
29
27
|
studio_url: Optional[Text]
|
|
30
28
|
client_id: Optional[Text]
|
|
31
29
|
realm_name: Optional[Text]
|
|
32
|
-
disable_verify: bool = False
|
|
33
30
|
|
|
34
|
-
def to_dict(self) -> Dict[Text, Optional[
|
|
31
|
+
def to_dict(self) -> Dict[Text, Optional[Text]]:
|
|
35
32
|
return {
|
|
36
33
|
AUTH_SERVER_URL_KEY: self.authentication_server_url,
|
|
37
34
|
STUDIO_URL_KEY: self.studio_url,
|
|
38
35
|
CLIENT_ID_KEY: self.client_id,
|
|
39
36
|
REALM_NAME_KEY: self.realm_name,
|
|
40
|
-
DISABLE_VERIFY: self.disable_verify,
|
|
41
37
|
}
|
|
42
38
|
|
|
43
39
|
@classmethod
|
|
@@ -47,7 +43,6 @@ class StudioConfig:
|
|
|
47
43
|
studio_url=data[STUDIO_URL_KEY],
|
|
48
44
|
client_id=data[CLIENT_ID_KEY],
|
|
49
45
|
realm_name=data[REALM_NAME_KEY],
|
|
50
|
-
disable_verify=data.get(DISABLE_VERIFY, False),
|
|
51
46
|
)
|
|
52
47
|
|
|
53
48
|
def write_config(self) -> None:
|
|
@@ -78,7 +73,7 @@ class StudioConfig:
|
|
|
78
73
|
config = read_global_config_value(STUDIO_CONFIG_KEY, unavailable_ok=True)
|
|
79
74
|
|
|
80
75
|
if config is None:
|
|
81
|
-
return StudioConfig(None, None, None, None
|
|
76
|
+
return StudioConfig(None, None, None, None)
|
|
82
77
|
|
|
83
78
|
if not isinstance(config, dict):
|
|
84
79
|
raise ValueError(
|
|
@@ -88,7 +83,7 @@ class StudioConfig:
|
|
|
88
83
|
)
|
|
89
84
|
|
|
90
85
|
for key in config:
|
|
91
|
-
if not isinstance(config[key], str)
|
|
86
|
+
if not isinstance(config[key], str):
|
|
92
87
|
raise ValueError(
|
|
93
88
|
"Invalid config file format. "
|
|
94
89
|
f"Key '{key}' is not a text value."
|
|
@@ -107,9 +102,6 @@ class StudioConfig:
|
|
|
107
102
|
studio_url=StudioConfig._read_env_value(RASA_STUDIO_CLI_STUDIO_URL_ENV),
|
|
108
103
|
client_id=StudioConfig._read_env_value(RASA_STUDIO_CLI_CLIENT_ID_KEY_ENV),
|
|
109
104
|
realm_name=StudioConfig._read_env_value(RASA_STUDIO_CLI_REALM_NAME_KEY_ENV),
|
|
110
|
-
disable_verify=bool(
|
|
111
|
-
os.getenv(RASA_STUDIO_CLI_DISABLE_VERIFY_KEY_ENV, False)
|
|
112
|
-
),
|
|
113
105
|
)
|
|
114
106
|
|
|
115
107
|
@staticmethod
|
|
@@ -132,5 +124,4 @@ class StudioConfig:
|
|
|
132
124
|
studio_url=self.studio_url or other.studio_url,
|
|
133
125
|
client_id=self.client_id or other.client_id,
|
|
134
126
|
realm_name=self.realm_name or other.realm_name,
|
|
135
|
-
disable_verify=self.disable_verify or other.disable_verify,
|
|
136
127
|
)
|
rasa/studio/constants.py
CHANGED
|
@@ -10,7 +10,6 @@ RASA_STUDIO_AUTH_SERVER_URL_ENV = "RASA_STUDIO_AUTH_SERVER_URL"
|
|
|
10
10
|
RASA_STUDIO_CLI_STUDIO_URL_ENV = "RASA_STUDIO_CLI_STUDIO_URL"
|
|
11
11
|
RASA_STUDIO_CLI_REALM_NAME_KEY_ENV = "RASA_STUDIO_CLI_REALM_NAME_KEY"
|
|
12
12
|
RASA_STUDIO_CLI_CLIENT_ID_KEY_ENV = "RASA_STUDIO_CLI_CLIENT_ID_KEY"
|
|
13
|
-
RASA_STUDIO_CLI_DISABLE_VERIFY_KEY_ENV = "RASA_STUDIO_CLI_DISABLE_VERIFY_KEY"
|
|
14
13
|
|
|
15
14
|
STUDIO_NLU_FILENAME = "studio_nlu.yml"
|
|
16
15
|
STUDIO_DOMAIN_FILENAME = "studio_domain.yml"
|
rasa/studio/data_handler.py
CHANGED
|
@@ -76,9 +76,7 @@ class StudioDataHandler:
|
|
|
76
76
|
|
|
77
77
|
return request
|
|
78
78
|
|
|
79
|
-
def _make_request(
|
|
80
|
-
self, GQL_req: Dict[Any, Any], verify: bool = True
|
|
81
|
-
) -> Dict[Any, Any]:
|
|
79
|
+
def _make_request(self, GQL_req: Dict[Any, Any]) -> Dict[Any, Any]:
|
|
82
80
|
token = KeycloakTokenReader().get_token()
|
|
83
81
|
if token.is_expired():
|
|
84
82
|
token = self.refresh_token(token)
|
|
@@ -95,7 +93,6 @@ class StudioDataHandler:
|
|
|
95
93
|
"Authorization": f"{token.token_type} {token.access_token}",
|
|
96
94
|
"Content-Type": "application/json",
|
|
97
95
|
},
|
|
98
|
-
verify=verify,
|
|
99
96
|
)
|
|
100
97
|
|
|
101
98
|
if res.status_code != 200:
|
|
@@ -131,9 +128,7 @@ class StudioDataHandler:
|
|
|
131
128
|
The data from Rasa Studio.
|
|
132
129
|
"""
|
|
133
130
|
GQL_req = self._build_request()
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
response = self._make_request(GQL_req, verify=verify)
|
|
131
|
+
response = self._make_request(GQL_req)
|
|
137
132
|
self._extract_data(response)
|
|
138
133
|
|
|
139
134
|
def request_data(
|
|
@@ -150,9 +145,7 @@ class StudioDataHandler:
|
|
|
150
145
|
The data from Rasa Studio.
|
|
151
146
|
"""
|
|
152
147
|
GQL_req = self._build_request(intent_names, entity_names)
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
response = self._make_request(GQL_req, verify=verify)
|
|
148
|
+
response = self._make_request(GQL_req)
|
|
156
149
|
self._extract_data(response)
|
|
157
150
|
|
|
158
151
|
def get_config(self) -> Optional[str]:
|
rasa/studio/upload.py
CHANGED
|
@@ -56,10 +56,7 @@ def _get_selected_entities_and_intents(
|
|
|
56
56
|
|
|
57
57
|
def handle_upload(args: argparse.Namespace) -> None:
|
|
58
58
|
"""Uploads primitives to rasa studio."""
|
|
59
|
-
|
|
60
|
-
endpoint = studio_config.studio_url
|
|
61
|
-
verify = not studio_config.disable_verify
|
|
62
|
-
|
|
59
|
+
endpoint = StudioConfig.read_config().studio_url
|
|
63
60
|
if not endpoint:
|
|
64
61
|
rasa.shared.utils.cli.print_error_and_exit(
|
|
65
62
|
"No GraphQL endpoint found in config. Please run `rasa studio config`."
|
|
@@ -79,9 +76,9 @@ def handle_upload(args: argparse.Namespace) -> None:
|
|
|
79
76
|
|
|
80
77
|
# check safely if args.calm is set and not fail if not
|
|
81
78
|
if hasattr(args, "calm") and args.calm:
|
|
82
|
-
upload_calm_assistant(args, endpoint
|
|
79
|
+
upload_calm_assistant(args, endpoint)
|
|
83
80
|
else:
|
|
84
|
-
upload_nlu_assistant(args, endpoint
|
|
81
|
+
upload_nlu_assistant(args, endpoint)
|
|
85
82
|
|
|
86
83
|
|
|
87
84
|
config_keys = [
|
|
@@ -129,9 +126,7 @@ def _get_assistant_name(config: Dict[Text, Any]) -> str:
|
|
|
129
126
|
|
|
130
127
|
|
|
131
128
|
@with_studio_error_handler
|
|
132
|
-
def upload_calm_assistant(
|
|
133
|
-
args: argparse.Namespace, endpoint: str, verify: bool = True
|
|
134
|
-
) -> StudioResult:
|
|
129
|
+
def upload_calm_assistant(args: argparse.Namespace, endpoint: str) -> StudioResult:
|
|
135
130
|
"""Uploads the CALM assistant data to Rasa Studio.
|
|
136
131
|
|
|
137
132
|
Args:
|
|
@@ -221,13 +216,11 @@ def upload_calm_assistant(
|
|
|
221
216
|
)
|
|
222
217
|
|
|
223
218
|
structlogger.info("Uploading to Rasa Studio...")
|
|
224
|
-
return make_request(endpoint, graphql_req
|
|
219
|
+
return make_request(endpoint, graphql_req)
|
|
225
220
|
|
|
226
221
|
|
|
227
222
|
@with_studio_error_handler
|
|
228
|
-
def upload_nlu_assistant(
|
|
229
|
-
args: argparse.Namespace, endpoint: str, verify: bool = True
|
|
230
|
-
) -> StudioResult:
|
|
223
|
+
def upload_nlu_assistant(args: argparse.Namespace, endpoint: str) -> StudioResult:
|
|
231
224
|
"""Uploads the classic (dm1) assistant data to Rasa Studio.
|
|
232
225
|
|
|
233
226
|
Args:
|
|
@@ -275,16 +268,15 @@ def upload_nlu_assistant(
|
|
|
275
268
|
graphql_req = build_request(assistant_name, nlu_examples_yaml, domain_yaml)
|
|
276
269
|
|
|
277
270
|
structlogger.info("Uploading to Rasa Studio...")
|
|
278
|
-
return make_request(endpoint, graphql_req
|
|
271
|
+
return make_request(endpoint, graphql_req)
|
|
279
272
|
|
|
280
273
|
|
|
281
|
-
def make_request(endpoint: str, graphql_req: Dict
|
|
274
|
+
def make_request(endpoint: str, graphql_req: Dict) -> StudioResult:
|
|
282
275
|
"""Makes a request to the studio endpoint to upload data.
|
|
283
276
|
|
|
284
277
|
Args:
|
|
285
278
|
endpoint: The studio endpoint
|
|
286
279
|
graphql_req: The graphql request
|
|
287
|
-
verify: Whether to verify SSL
|
|
288
280
|
"""
|
|
289
281
|
token = KeycloakTokenReader().get_token()
|
|
290
282
|
res = requests.post(
|
|
@@ -294,7 +286,6 @@ def make_request(endpoint: str, graphql_req: Dict, verify: bool = True) -> Studi
|
|
|
294
286
|
"Authorization": f"{token.token_type} {token.access_token}",
|
|
295
287
|
"Content-Type": "application/json",
|
|
296
288
|
},
|
|
297
|
-
verify=verify,
|
|
298
289
|
)
|
|
299
290
|
|
|
300
291
|
if results_logger.response_has_errors(res.json()):
|
|
@@ -664,7 +664,7 @@ def extract_attrs_for_custom_action_executor_run(
|
|
|
664
664
|
|
|
665
665
|
attrs: Dict[str, Any] = {
|
|
666
666
|
"class_name": self.__class__.__name__,
|
|
667
|
-
"action_name": self.action_name,
|
|
667
|
+
"action_name": self.action_name if hasattr(self, "action_name") else "None",
|
|
668
668
|
"sender_id": tracker.sender_id,
|
|
669
669
|
"url": str(url),
|
|
670
670
|
"actions_module": str(actions_module),
|
rasa/utils/io.py
CHANGED
|
@@ -2,6 +2,7 @@ import asyncio
|
|
|
2
2
|
import filecmp
|
|
3
3
|
import logging
|
|
4
4
|
import os
|
|
5
|
+
import pickle
|
|
5
6
|
import tempfile
|
|
6
7
|
import warnings
|
|
7
8
|
import re
|
|
@@ -97,6 +98,29 @@ def enable_async_loop_debugging(
|
|
|
97
98
|
return event_loop
|
|
98
99
|
|
|
99
100
|
|
|
101
|
+
def pickle_dump(filename: Union[Text, Path], obj: Any) -> None:
|
|
102
|
+
"""Saves object to file.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
filename: the filename to save the object to
|
|
106
|
+
obj: the object to store
|
|
107
|
+
"""
|
|
108
|
+
with open(filename, "wb") as f:
|
|
109
|
+
pickle.dump(obj, f)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def pickle_load(filename: Union[Text, Path]) -> Any:
|
|
113
|
+
"""Loads an object from a file.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
filename: the filename to load the object from
|
|
117
|
+
|
|
118
|
+
Returns: the loaded object
|
|
119
|
+
"""
|
|
120
|
+
with open(filename, "rb") as f:
|
|
121
|
+
return pickle.load(f)
|
|
122
|
+
|
|
123
|
+
|
|
100
124
|
def create_temporary_file(data: Any, suffix: Text = "", mode: Text = "w+") -> Text:
|
|
101
125
|
"""Creates a tempfile.NamedTemporaryFile object for data."""
|
|
102
126
|
encoding = None if "b" in mode else rasa.shared.utils.io.DEFAULT_ENCODING
|
|
@@ -167,6 +191,48 @@ def create_validator(
|
|
|
167
191
|
return FunctionValidator
|
|
168
192
|
|
|
169
193
|
|
|
194
|
+
def json_unpickle(
|
|
195
|
+
file_name: Union[Text, Path], encode_non_string_keys: bool = False
|
|
196
|
+
) -> Any:
|
|
197
|
+
"""Unpickle an object from file using json.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
file_name: the file to load the object from
|
|
201
|
+
encode_non_string_keys: If set to `True` then jsonpickle will encode non-string
|
|
202
|
+
dictionary keys instead of coercing them into strings via `repr()`.
|
|
203
|
+
|
|
204
|
+
Returns: the object
|
|
205
|
+
"""
|
|
206
|
+
import jsonpickle.ext.numpy as jsonpickle_numpy
|
|
207
|
+
import jsonpickle
|
|
208
|
+
|
|
209
|
+
jsonpickle_numpy.register_handlers()
|
|
210
|
+
|
|
211
|
+
file_content = rasa.shared.utils.io.read_file(file_name)
|
|
212
|
+
return jsonpickle.loads(file_content, keys=encode_non_string_keys)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def json_pickle(
|
|
216
|
+
file_name: Union[Text, Path], obj: Any, encode_non_string_keys: bool = False
|
|
217
|
+
) -> None:
|
|
218
|
+
"""Pickle an object to a file using json.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
file_name: the file to store the object to
|
|
222
|
+
obj: the object to store
|
|
223
|
+
encode_non_string_keys: If set to `True` then jsonpickle will encode non-string
|
|
224
|
+
dictionary keys instead of coercing them into strings via `repr()`.
|
|
225
|
+
"""
|
|
226
|
+
import jsonpickle.ext.numpy as jsonpickle_numpy
|
|
227
|
+
import jsonpickle
|
|
228
|
+
|
|
229
|
+
jsonpickle_numpy.register_handlers()
|
|
230
|
+
|
|
231
|
+
rasa.shared.utils.io.write_text_file(
|
|
232
|
+
jsonpickle.dumps(obj, keys=encode_non_string_keys), file_name
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
|
|
170
236
|
def get_emoji_regex() -> Pattern:
|
|
171
237
|
"""Returns regex to identify emojis."""
|
|
172
238
|
return re.compile(
|
|
@@ -20,8 +20,6 @@ import numpy as np
|
|
|
20
20
|
import scipy.sparse
|
|
21
21
|
from sklearn.model_selection import train_test_split
|
|
22
22
|
|
|
23
|
-
from rasa.utils.tensorflow.feature_array import FeatureArray
|
|
24
|
-
|
|
25
23
|
logger = logging.getLogger(__name__)
|
|
26
24
|
|
|
27
25
|
|
|
@@ -39,6 +37,199 @@ def ragged_array_to_ndarray(ragged_array: Iterable[np.ndarray]) -> np.ndarray:
|
|
|
39
37
|
return np.array(ragged_array, dtype=object)
|
|
40
38
|
|
|
41
39
|
|
|
40
|
+
class FeatureArray(np.ndarray):
|
|
41
|
+
"""Stores any kind of features ready to be used by a RasaModel.
|
|
42
|
+
|
|
43
|
+
Next to the input numpy array of features, it also received the number of
|
|
44
|
+
dimensions of the features.
|
|
45
|
+
As our features can have 1 to 4 dimensions we might have different number of numpy
|
|
46
|
+
arrays stacked. The number of dimensions helps us to figure out how to handle this
|
|
47
|
+
particular feature array. Also, it is automatically determined whether the feature
|
|
48
|
+
array is sparse or not and the number of units is determined as well.
|
|
49
|
+
|
|
50
|
+
Subclassing np.array: https://numpy.org/doc/stable/user/basics.subclassing.html
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __new__(
|
|
54
|
+
cls, input_array: np.ndarray, number_of_dimensions: int
|
|
55
|
+
) -> "FeatureArray":
|
|
56
|
+
"""Create and return a new object. See help(type) for accurate signature."""
|
|
57
|
+
FeatureArray._validate_number_of_dimensions(number_of_dimensions, input_array)
|
|
58
|
+
|
|
59
|
+
feature_array = np.asarray(input_array).view(cls)
|
|
60
|
+
|
|
61
|
+
if number_of_dimensions <= 2:
|
|
62
|
+
feature_array.units = input_array.shape[-1]
|
|
63
|
+
feature_array.is_sparse = isinstance(input_array[0], scipy.sparse.spmatrix)
|
|
64
|
+
elif number_of_dimensions == 3:
|
|
65
|
+
feature_array.units = input_array[0].shape[-1]
|
|
66
|
+
feature_array.is_sparse = isinstance(input_array[0], scipy.sparse.spmatrix)
|
|
67
|
+
elif number_of_dimensions == 4:
|
|
68
|
+
feature_array.units = input_array[0][0].shape[-1]
|
|
69
|
+
feature_array.is_sparse = isinstance(
|
|
70
|
+
input_array[0][0], scipy.sparse.spmatrix
|
|
71
|
+
)
|
|
72
|
+
else:
|
|
73
|
+
raise ValueError(
|
|
74
|
+
f"Number of dimensions '{number_of_dimensions}' currently not "
|
|
75
|
+
f"supported."
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
feature_array.number_of_dimensions = number_of_dimensions
|
|
79
|
+
|
|
80
|
+
return feature_array
|
|
81
|
+
|
|
82
|
+
def __init__(
|
|
83
|
+
self, input_array: Any, number_of_dimensions: int, **kwargs: Any
|
|
84
|
+
) -> None:
|
|
85
|
+
"""Initialize. FeatureArray.
|
|
86
|
+
|
|
87
|
+
Needed in order to avoid 'Invalid keyword argument number_of_dimensions
|
|
88
|
+
to function FeatureArray.__init__ '
|
|
89
|
+
Args:
|
|
90
|
+
input_array: the array that contains features
|
|
91
|
+
number_of_dimensions: number of dimensions in input_array
|
|
92
|
+
"""
|
|
93
|
+
super().__init__(**kwargs)
|
|
94
|
+
self.number_of_dimensions = number_of_dimensions
|
|
95
|
+
|
|
96
|
+
def __array_finalize__(self, obj: Optional[np.ndarray]) -> None:
|
|
97
|
+
"""This method is called when the system allocates a new array from obj.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
obj: A subclass (subtype) of ndarray.
|
|
101
|
+
"""
|
|
102
|
+
if obj is None:
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
self.units = getattr(obj, "units", None)
|
|
106
|
+
self.number_of_dimensions = getattr(obj, "number_of_dimensions", None) # type: ignore[assignment]
|
|
107
|
+
self.is_sparse = getattr(obj, "is_sparse", None)
|
|
108
|
+
|
|
109
|
+
default_attributes = {
|
|
110
|
+
"units": self.units,
|
|
111
|
+
"number_of_dimensions": self.number_of_dimensions,
|
|
112
|
+
"is_spare": self.is_sparse,
|
|
113
|
+
}
|
|
114
|
+
self.__dict__.update(default_attributes)
|
|
115
|
+
|
|
116
|
+
# pytype: disable=attribute-error
|
|
117
|
+
def __array_ufunc__(
|
|
118
|
+
self, ufunc: Any, method: Text, *inputs: Any, **kwargs: Any
|
|
119
|
+
) -> Any:
|
|
120
|
+
"""Overwrite this method as we are subclassing numpy array.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
ufunc: The ufunc object that was called.
|
|
124
|
+
method: A string indicating which Ufunc method was called
|
|
125
|
+
(one of "__call__", "reduce", "reduceat", "accumulate", "outer",
|
|
126
|
+
"inner").
|
|
127
|
+
*inputs: A tuple of the input arguments to the ufunc.
|
|
128
|
+
**kwargs: Any additional arguments
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
The result of the operation.
|
|
132
|
+
"""
|
|
133
|
+
f = {
|
|
134
|
+
"reduce": ufunc.reduce,
|
|
135
|
+
"accumulate": ufunc.accumulate,
|
|
136
|
+
"reduceat": ufunc.reduceat,
|
|
137
|
+
"outer": ufunc.outer,
|
|
138
|
+
"at": ufunc.at,
|
|
139
|
+
"__call__": ufunc,
|
|
140
|
+
}
|
|
141
|
+
# convert the inputs to np.ndarray to prevent recursion, call the function,
|
|
142
|
+
# then cast it back as FeatureArray
|
|
143
|
+
output = FeatureArray(
|
|
144
|
+
f[method](*(i.view(np.ndarray) for i in inputs), **kwargs),
|
|
145
|
+
number_of_dimensions=kwargs["number_of_dimensions"],
|
|
146
|
+
)
|
|
147
|
+
output.__dict__ = self.__dict__ # carry forward attributes
|
|
148
|
+
return output
|
|
149
|
+
|
|
150
|
+
def __reduce__(self) -> Tuple[Any, Any, Any]:
|
|
151
|
+
"""Needed in order to pickle this object.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
A tuple.
|
|
155
|
+
"""
|
|
156
|
+
pickled_state = super(FeatureArray, self).__reduce__()
|
|
157
|
+
if isinstance(pickled_state, str):
|
|
158
|
+
raise TypeError("np array __reduce__ returned string instead of tuple.")
|
|
159
|
+
new_state = pickled_state[2] + (
|
|
160
|
+
self.number_of_dimensions,
|
|
161
|
+
self.is_sparse,
|
|
162
|
+
self.units,
|
|
163
|
+
)
|
|
164
|
+
return pickled_state[0], pickled_state[1], new_state
|
|
165
|
+
|
|
166
|
+
def __setstate__(self, state: Any, **kwargs: Any) -> None:
|
|
167
|
+
"""Sets the state.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
state: The state argument must be a sequence that contains the following
|
|
171
|
+
elements version, shape, dtype, isFortan, rawdata.
|
|
172
|
+
**kwargs: Any additional parameter
|
|
173
|
+
"""
|
|
174
|
+
# Needed in order to load the object
|
|
175
|
+
self.number_of_dimensions = state[-3]
|
|
176
|
+
self.is_sparse = state[-2]
|
|
177
|
+
self.units = state[-1]
|
|
178
|
+
super(FeatureArray, self).__setstate__(state[0:-3], **kwargs)
|
|
179
|
+
|
|
180
|
+
# pytype: enable=attribute-error
|
|
181
|
+
|
|
182
|
+
@staticmethod
|
|
183
|
+
def _validate_number_of_dimensions(
|
|
184
|
+
number_of_dimensions: int, input_array: np.ndarray
|
|
185
|
+
) -> None:
|
|
186
|
+
"""Validates if the the input array has given number of dimensions.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
number_of_dimensions: number of dimensions
|
|
190
|
+
input_array: input array
|
|
191
|
+
|
|
192
|
+
Raises: ValueError in case the dimensions do not match
|
|
193
|
+
"""
|
|
194
|
+
_sub_array = input_array
|
|
195
|
+
dim = 0
|
|
196
|
+
# Go number_of_dimensions into the given input_array
|
|
197
|
+
for i in range(1, number_of_dimensions + 1):
|
|
198
|
+
_sub_array = _sub_array[0]
|
|
199
|
+
if isinstance(_sub_array, scipy.sparse.spmatrix):
|
|
200
|
+
dim = i
|
|
201
|
+
break
|
|
202
|
+
if isinstance(_sub_array, np.ndarray) and _sub_array.shape[0] == 0:
|
|
203
|
+
# sequence dimension is 0, we are dealing with "fake" features
|
|
204
|
+
dim = i
|
|
205
|
+
break
|
|
206
|
+
|
|
207
|
+
# If the resulting sub_array is sparse, the remaining number of dimensions
|
|
208
|
+
# should be at least 2
|
|
209
|
+
if isinstance(_sub_array, scipy.sparse.spmatrix):
|
|
210
|
+
if dim > 2:
|
|
211
|
+
raise ValueError(
|
|
212
|
+
f"Given number of dimensions '{number_of_dimensions}' does not "
|
|
213
|
+
f"match dimensions of given input array: {input_array}."
|
|
214
|
+
)
|
|
215
|
+
elif isinstance(_sub_array, np.ndarray) and _sub_array.shape[0] == 0:
|
|
216
|
+
# sequence dimension is 0, we are dealing with "fake" features,
|
|
217
|
+
# but they should be of dim 2
|
|
218
|
+
if dim > 2:
|
|
219
|
+
raise ValueError(
|
|
220
|
+
f"Given number of dimensions '{number_of_dimensions}' does not "
|
|
221
|
+
f"match dimensions of given input array: {input_array}."
|
|
222
|
+
)
|
|
223
|
+
# If the resulting sub_array is dense, the sub_array should be a single number
|
|
224
|
+
elif not np.issubdtype(type(_sub_array), np.integer) and not isinstance(
|
|
225
|
+
_sub_array, (np.float32, np.float64)
|
|
226
|
+
):
|
|
227
|
+
raise ValueError(
|
|
228
|
+
f"Given number of dimensions '{number_of_dimensions}' does not match "
|
|
229
|
+
f"dimensions of given input array: {input_array}."
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
|
|
42
233
|
class FeatureSignature(NamedTuple):
|
|
43
234
|
"""Signature of feature arrays.
|
|
44
235
|
|
rasa/validator.py
CHANGED
|
@@ -303,18 +303,6 @@ class Validator:
|
|
|
303
303
|
everything_is_alright = True
|
|
304
304
|
|
|
305
305
|
for response_text, response_variations in self.domain.responses.items():
|
|
306
|
-
if not response_variations:
|
|
307
|
-
structlogger.error(
|
|
308
|
-
"validator.empty_response",
|
|
309
|
-
response=response_text,
|
|
310
|
-
event_info=(
|
|
311
|
-
f"The response '{response_text}' in the domain file "
|
|
312
|
-
f"does not have any variations. Please add at least one "
|
|
313
|
-
f"variation to the response."
|
|
314
|
-
),
|
|
315
|
-
)
|
|
316
|
-
everything_is_alright = False
|
|
317
|
-
|
|
318
306
|
for response in response_variations:
|
|
319
307
|
if any(
|
|
320
308
|
self.check_for_placeholder(response.get(key))
|
rasa/version.py
CHANGED