rasa-pro 3.10.15__py3-none-any.whl → 3.11.0__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.
- rasa/__main__.py +31 -15
- rasa/api.py +12 -2
- rasa/cli/arguments/default_arguments.py +24 -4
- rasa/cli/arguments/run.py +15 -0
- rasa/cli/arguments/shell.py +5 -1
- rasa/cli/arguments/train.py +17 -9
- rasa/cli/evaluate.py +7 -7
- rasa/cli/inspect.py +19 -7
- rasa/cli/interactive.py +1 -0
- rasa/cli/project_templates/calm/config.yml +5 -7
- rasa/cli/project_templates/calm/endpoints.yml +15 -2
- rasa/cli/project_templates/tutorial/config.yml +8 -5
- rasa/cli/project_templates/tutorial/data/flows.yml +1 -1
- rasa/cli/project_templates/tutorial/data/patterns.yml +5 -0
- rasa/cli/project_templates/tutorial/domain.yml +14 -0
- rasa/cli/project_templates/tutorial/endpoints.yml +5 -0
- rasa/cli/run.py +7 -0
- rasa/cli/scaffold.py +4 -2
- rasa/cli/studio/upload.py +0 -15
- rasa/cli/train.py +14 -53
- rasa/cli/utils.py +14 -11
- rasa/cli/x.py +7 -7
- rasa/constants.py +3 -1
- rasa/core/actions/action.py +77 -33
- rasa/core/actions/action_hangup.py +29 -0
- rasa/core/actions/action_repeat_bot_messages.py +89 -0
- rasa/core/actions/e2e_stub_custom_action_executor.py +5 -1
- rasa/core/actions/http_custom_action_executor.py +4 -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 +10 -6
- rasa/core/channels/channel.py +41 -4
- rasa/core/channels/development_inspector.py +150 -46
- rasa/core/channels/inspector/README.md +1 -1
- rasa/core/channels/inspector/dist/assets/{arc-b6e548fe.js → arc-bc141fb2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-d0fbc5ce-fa03ac9e.js → c4Diagram-d0fbc5ce-be2db283.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-936ed81e-ee67392a.js → classDiagram-936ed81e-55366915.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-c3cb15f1-9b283fae.js → classDiagram-v2-c3cb15f1-bb529518.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{createText-62fc7601-8b6fcc2a.js → createText-62fc7601-b0ec81d6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-f2ad444c-22e77f4f.js → edges-f2ad444c-6166330c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9d236eb7-60ffc87f.js → erDiagram-9d236eb7-5ccc6a8e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-1972c806-9dd802e4.js → flowDb-1972c806-fca3bfe4.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-7ea5b25a-5fa1912f.js → flowDiagram-7ea5b25a-4739080f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-736177bf.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-abe16c3d-622a1fd2.js → flowchart-elk-definition-abe16c3d-7c1b0e0f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-9b5ea136-e285a63a.js → ganttDiagram-9b5ea136-772fd050.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-99d0ae7c-f237bdca.js → gitGraphDiagram-99d0ae7c-8eae1dc9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-2c4b9a3b-4b03d70e.js → index-2c4b9a3b-f55afcdf.js} +1 -1
- rasa/core/channels/inspector/dist/assets/index-e7cef9de.js +1317 -0
- rasa/core/channels/inspector/dist/assets/{infoDiagram-736b4530-72a0fa5f.js → infoDiagram-736b4530-124d4a14.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-df861f2b-82218c41.js → journeyDiagram-df861f2b-7c4fae44.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-78cff630.js → layout-b9885fb6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-5038b469.js → line-7c59abb6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-c4fc4098.js → linear-4776f780.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-beec6740-c33c8ea6.js → mindmap-definition-beec6740-2332c46c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-dbbf0591-a8d03059.js → pieDiagram-dbbf0591-8fb39303.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-4d7f4fd6-6a0e56b2.js → quadrantDiagram-4d7f4fd6-3c7180a2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-6fc4c22a-2dc7c7bd.js → requirementDiagram-6fc4c22a-e910bcb8.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-8f13d901-2360fe39.js → sankeyDiagram-8f13d901-ead16c89.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-b655622a-41b9f9ad.js → sequenceDiagram-b655622a-29a02a19.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-59f0c015-0aad326f.js → stateDiagram-59f0c015-042b3137.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-2b26beab-9847d984.js → stateDiagram-v2-2b26beab-2178c0f3.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-080da4f6-564d890e.js → styles-080da4f6-23ffa4fc.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-3dcbcfbf-38957613.js → styles-3dcbcfbf-94f59763.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9c745c82-f0fc6921.js → styles-9c745c82-78a6bebc.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-4835440b-ef3c5a77.js → svgDrawCommon-4835440b-eae2a6f6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-5b62e21b-bf3e91c1.js → timeline-definition-5b62e21b-5c968d92.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-2b33534f-4d4026c0.js → xychartDiagram-2b33534f-fd3db0d5.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +18 -15
- rasa/core/channels/inspector/index.html +17 -14
- rasa/core/channels/inspector/package.json +5 -1
- rasa/core/channels/inspector/src/App.tsx +118 -68
- 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 +6 -3
- rasa/core/channels/inspector/src/helpers/audiostream.ts +165 -0
- 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 +28 -1
- rasa/core/channels/telegram.py +1 -1
- rasa/core/channels/twilio.py +1 -1
- rasa/core/channels/{audiocodes.py → voice_ready/audiocodes.py} +122 -69
- rasa/core/channels/{voice_aware → voice_ready}/jambonz.py +26 -8
- rasa/core/channels/{voice_aware → voice_ready}/jambonz_protocol.py +57 -5
- rasa/core/channels/{twilio_voice.py → voice_ready/twilio_voice.py} +64 -28
- rasa/core/channels/voice_ready/utils.py +37 -0
- rasa/core/channels/voice_stream/asr/__init__.py +0 -0
- rasa/core/channels/voice_stream/asr/asr_engine.py +89 -0
- rasa/core/channels/voice_stream/asr/asr_event.py +18 -0
- rasa/core/channels/voice_stream/asr/azure.py +129 -0
- rasa/core/channels/voice_stream/asr/deepgram.py +90 -0
- rasa/core/channels/voice_stream/audio_bytes.py +8 -0
- rasa/core/channels/voice_stream/browser_audio.py +107 -0
- rasa/core/channels/voice_stream/call_state.py +23 -0
- rasa/core/channels/voice_stream/tts/__init__.py +0 -0
- rasa/core/channels/voice_stream/tts/azure.py +106 -0
- rasa/core/channels/voice_stream/tts/cartesia.py +118 -0
- rasa/core/channels/voice_stream/tts/tts_cache.py +27 -0
- rasa/core/channels/voice_stream/tts/tts_engine.py +58 -0
- rasa/core/channels/voice_stream/twilio_media_streams.py +173 -0
- rasa/core/channels/voice_stream/util.py +57 -0
- rasa/core/channels/voice_stream/voice_channel.py +427 -0
- rasa/core/information_retrieval/qdrant.py +1 -0
- rasa/core/nlg/contextual_response_rephraser.py +45 -17
- rasa/{nlu → core}/persistor.py +203 -68
- rasa/core/policies/enterprise_search_policy.py +119 -63
- rasa/core/policies/flows/flow_executor.py +15 -22
- rasa/core/policies/intentless_policy.py +83 -28
- rasa/core/processor.py +25 -0
- rasa/core/run.py +12 -2
- 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 +33 -34
- rasa/core/utils.py +47 -21
- rasa/dialogue_understanding/coexistence/llm_based_router.py +41 -14
- rasa/dialogue_understanding/commands/__init__.py +6 -0
- rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +60 -0
- rasa/dialogue_understanding/commands/session_end_command.py +61 -0
- rasa/dialogue_understanding/commands/user_silence_command.py +59 -0
- rasa/dialogue_understanding/commands/utils.py +5 -0
- rasa/dialogue_understanding/generator/constants.py +2 -0
- rasa/dialogue_understanding/generator/flow_retrieval.py +47 -9
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +38 -15
- rasa/dialogue_understanding/generator/llm_command_generator.py +1 -1
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +35 -13
- rasa/dialogue_understanding/generator/single_step/command_prompt_template.jinja2 +3 -0
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +60 -13
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +53 -0
- rasa/dialogue_understanding/patterns/repeat.py +37 -0
- rasa/dialogue_understanding/patterns/user_silence.py +37 -0
- rasa/dialogue_understanding/processor/command_processor.py +21 -1
- rasa/e2e_test/aggregate_test_stats_calculator.py +1 -11
- rasa/e2e_test/assertions.py +136 -61
- rasa/e2e_test/assertions_schema.yml +23 -0
- rasa/e2e_test/e2e_test_case.py +85 -6
- rasa/e2e_test/e2e_test_runner.py +2 -3
- rasa/engine/graph.py +0 -1
- rasa/engine/loader.py +12 -0
- 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 +527 -74
- rasa/model_manager/__init__.py +0 -0
- rasa/model_manager/config.py +40 -0
- rasa/model_manager/model_api.py +559 -0
- rasa/model_manager/runner_service.py +286 -0
- rasa/model_manager/socket_bridge.py +146 -0
- rasa/model_manager/studio_jwt_auth.py +86 -0
- rasa/model_manager/trainer_service.py +325 -0
- rasa/model_manager/utils.py +87 -0
- rasa/model_manager/warm_rasa_process.py +187 -0
- rasa/model_service.py +112 -0
- rasa/model_training.py +42 -23
- rasa/nlu/tokenizers/whitespace_tokenizer.py +3 -14
- rasa/server.py +4 -2
- rasa/shared/constants.py +60 -8
- rasa/shared/core/constants.py +13 -0
- rasa/shared/core/domain.py +107 -50
- rasa/shared/core/events.py +29 -0
- rasa/shared/core/flows/flow.py +5 -0
- rasa/shared/core/flows/flows_list.py +19 -6
- rasa/shared/core/flows/flows_yaml_schema.json +10 -0
- rasa/shared/core/flows/utils.py +39 -0
- rasa/shared/core/flows/validation.py +121 -0
- rasa/shared/core/flows/yaml_flows_io.py +15 -27
- rasa/shared/core/slots.py +5 -0
- rasa/shared/importers/importer.py +59 -41
- rasa/shared/importers/multi_project.py +23 -11
- rasa/shared/importers/rasa.py +12 -3
- rasa/shared/importers/remote_importer.py +196 -0
- rasa/shared/importers/utils.py +3 -1
- rasa/shared/nlu/training_data/formats/rasa_yaml.py +18 -3
- rasa/shared/nlu/training_data/training_data.py +18 -19
- rasa/shared/providers/_configs/litellm_router_client_config.py +220 -0
- rasa/shared/providers/_configs/model_group_config.py +167 -0
- rasa/shared/providers/_configs/openai_client_config.py +1 -1
- rasa/shared/providers/_configs/rasa_llm_client_config.py +73 -0
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +1 -0
- rasa/shared/providers/_configs/utils.py +16 -0
- rasa/shared/providers/_utils.py +79 -0
- rasa/shared/providers/embedding/_base_litellm_embedding_client.py +13 -29
- rasa/shared/providers/embedding/azure_openai_embedding_client.py +54 -21
- rasa/shared/providers/embedding/default_litellm_embedding_client.py +24 -0
- rasa/shared/providers/embedding/litellm_router_embedding_client.py +135 -0
- rasa/shared/providers/llm/_base_litellm_client.py +34 -22
- rasa/shared/providers/llm/azure_openai_llm_client.py +50 -29
- rasa/shared/providers/llm/default_litellm_llm_client.py +24 -0
- rasa/shared/providers/llm/litellm_router_llm_client.py +182 -0
- rasa/shared/providers/llm/rasa_llm_client.py +112 -0
- rasa/shared/providers/llm/self_hosted_llm_client.py +5 -29
- rasa/shared/providers/mappings.py +19 -0
- rasa/shared/providers/router/__init__.py +0 -0
- rasa/shared/providers/router/_base_litellm_router_client.py +183 -0
- rasa/shared/providers/router/router_client.py +73 -0
- rasa/shared/utils/common.py +40 -24
- rasa/shared/utils/health_check/__init__.py +0 -0
- rasa/shared/utils/health_check/embeddings_health_check_mixin.py +31 -0
- rasa/shared/utils/health_check/health_check.py +258 -0
- rasa/shared/utils/health_check/llm_health_check_mixin.py +31 -0
- rasa/shared/utils/io.py +27 -6
- rasa/shared/utils/llm.py +353 -43
- rasa/shared/utils/schemas/events.py +2 -0
- rasa/shared/utils/schemas/model_config.yml +0 -10
- rasa/shared/utils/yaml.py +181 -38
- rasa/studio/data_handler.py +3 -1
- rasa/studio/upload.py +160 -74
- rasa/telemetry.py +94 -17
- rasa/tracing/config.py +3 -1
- rasa/tracing/instrumentation/attribute_extractors.py +95 -18
- rasa/tracing/instrumentation/instrumentation.py +121 -0
- rasa/utils/common.py +5 -0
- rasa/utils/endpoints.py +27 -1
- rasa/utils/io.py +8 -16
- rasa/utils/log_utils.py +9 -2
- rasa/utils/sanic_error_handler.py +32 -0
- rasa/validator.py +110 -4
- rasa/version.py +1 -1
- {rasa_pro-3.10.15.dist-info → rasa_pro-3.11.0.dist-info}/METADATA +14 -12
- {rasa_pro-3.10.15.dist-info → rasa_pro-3.11.0.dist-info}/RECORD +234 -183
- 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/core/channels/voice_aware/utils.py +0 -20
- rasa/llm_fine_tuning/notebooks/unsloth_finetuning.ipynb +0 -407
- /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.15.dist-info → rasa_pro-3.11.0.dist-info}/NOTICE +0 -0
- {rasa_pro-3.10.15.dist-info → rasa_pro-3.11.0.dist-info}/WHEEL +0 -0
- {rasa_pro-3.10.15.dist-info → rasa_pro-3.11.0.dist-info}/entry_points.txt +0 -0
rasa/shared/utils/yaml.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import datetime
|
|
2
|
+
import io
|
|
2
3
|
import logging
|
|
3
4
|
import os
|
|
4
5
|
import re
|
|
@@ -8,19 +9,13 @@ from dataclasses import field
|
|
|
8
9
|
from functools import lru_cache
|
|
9
10
|
from io import StringIO
|
|
10
11
|
from pathlib import Path
|
|
11
|
-
from typing import
|
|
12
|
+
from typing import Any, List, Optional, Tuple, Dict, Callable, Union
|
|
12
13
|
|
|
13
14
|
import jsonschema
|
|
14
15
|
from importlib_resources import files
|
|
15
16
|
from packaging import version
|
|
16
17
|
from pykwalify.core import Core
|
|
17
18
|
from pykwalify.errors import SchemaError
|
|
18
|
-
from ruamel import yaml as yaml
|
|
19
|
-
from ruamel.yaml import RoundTripRepresenter, YAMLError
|
|
20
|
-
from ruamel.yaml.constructor import DuplicateKeyError, BaseConstructor, ScalarNode
|
|
21
|
-
from ruamel.yaml.comments import CommentedSeq, CommentedMap
|
|
22
|
-
from ruamel.yaml.loader import SafeLoader
|
|
23
|
-
|
|
24
19
|
from rasa.shared.constants import (
|
|
25
20
|
ASSERTIONS_SCHEMA_EXTENSIONS_FILE,
|
|
26
21
|
ASSERTIONS_SCHEMA_FILE,
|
|
@@ -31,6 +26,7 @@ from rasa.shared.constants import (
|
|
|
31
26
|
LATEST_TRAINING_DATA_FORMAT_VERSION,
|
|
32
27
|
SCHEMA_EXTENSIONS_FILE,
|
|
33
28
|
RESPONSES_SCHEMA_FILE,
|
|
29
|
+
SENSITIVE_DATA,
|
|
34
30
|
)
|
|
35
31
|
from rasa.shared.exceptions import (
|
|
36
32
|
YamlException,
|
|
@@ -50,6 +46,11 @@ from rasa.shared.utils.io import (
|
|
|
50
46
|
raise_warning,
|
|
51
47
|
read_json_file,
|
|
52
48
|
)
|
|
49
|
+
from ruamel import yaml as yaml
|
|
50
|
+
from ruamel.yaml import YAML, RoundTripRepresenter, YAMLError
|
|
51
|
+
from ruamel.yaml.comments import CommentedSeq, CommentedMap
|
|
52
|
+
from ruamel.yaml.constructor import DuplicateKeyError, BaseConstructor, ScalarNode
|
|
53
|
+
from ruamel.yaml.loader import SafeLoader
|
|
53
54
|
|
|
54
55
|
logger = logging.getLogger(__name__)
|
|
55
56
|
|
|
@@ -62,8 +63,17 @@ READ_YAML_FILE_CACHE_MAXSIZE = os.environ.get(
|
|
|
62
63
|
|
|
63
64
|
@dataclass
|
|
64
65
|
class PathWithError:
|
|
66
|
+
"""Represents a validation error at a specific location in the YAML content.
|
|
67
|
+
|
|
68
|
+
Attributes:
|
|
69
|
+
message (str): A description of the validation error.
|
|
70
|
+
path (List[str]): Path to the node where the error occurred.
|
|
71
|
+
key (Optional[str]): The specific key associated with the error, if any.
|
|
72
|
+
"""
|
|
73
|
+
|
|
65
74
|
message: str
|
|
66
75
|
path: List[str] = field(default_factory=list)
|
|
76
|
+
key: Optional[str] = None
|
|
67
77
|
|
|
68
78
|
|
|
69
79
|
def fix_yaml_loader() -> None:
|
|
@@ -87,6 +97,12 @@ def replace_environment_variables() -> None:
|
|
|
87
97
|
def env_var_constructor(loader: BaseConstructor, node: ScalarNode) -> str:
|
|
88
98
|
"""Process environment variables found in the YAML."""
|
|
89
99
|
value = loader.construct_scalar(node)
|
|
100
|
+
|
|
101
|
+
# get key of current node
|
|
102
|
+
key_node = list(loader.constructed_objects)[-1]
|
|
103
|
+
if isinstance(key_node, ScalarNode) and key_node.value in SENSITIVE_DATA:
|
|
104
|
+
return value
|
|
105
|
+
|
|
90
106
|
expanded_vars = os.path.expandvars(value)
|
|
91
107
|
not_expanded = [
|
|
92
108
|
w for w in expanded_vars.split() if w.startswith("$") and w in value
|
|
@@ -104,7 +120,6 @@ def replace_environment_variables() -> None:
|
|
|
104
120
|
|
|
105
121
|
|
|
106
122
|
fix_yaml_loader()
|
|
107
|
-
replace_environment_variables()
|
|
108
123
|
|
|
109
124
|
|
|
110
125
|
class YamlValidationException(YamlException, ValueError):
|
|
@@ -138,21 +153,72 @@ class YamlValidationException(YamlException, ValueError):
|
|
|
138
153
|
if self.validation_errors:
|
|
139
154
|
unique_errors = {}
|
|
140
155
|
for error in self.validation_errors:
|
|
141
|
-
line_number = self._line_number_for_path(
|
|
156
|
+
line_number = self._line_number_for_path(
|
|
157
|
+
self.content, error.path, error.key
|
|
158
|
+
)
|
|
142
159
|
|
|
143
160
|
if line_number and self.filename:
|
|
144
|
-
|
|
161
|
+
error_location = f" in {self.filename}:{line_number}:\n"
|
|
145
162
|
elif line_number:
|
|
146
|
-
|
|
163
|
+
error_location = f" in Line {line_number}:\n"
|
|
147
164
|
else:
|
|
148
|
-
|
|
165
|
+
error_location = ""
|
|
149
166
|
|
|
150
|
-
|
|
151
|
-
|
|
167
|
+
code_snippet = self._get_code_snippet(line_number)
|
|
168
|
+
error_message = f"{error_location}\n{code_snippet}{error.message}\n"
|
|
169
|
+
unique_errors[error.message] = error_message
|
|
152
170
|
error_msg = "\n".join(unique_errors.values())
|
|
153
171
|
msg += f":\n{error_msg}"
|
|
154
172
|
return msg
|
|
155
173
|
|
|
174
|
+
def _get_code_snippet(
|
|
175
|
+
self,
|
|
176
|
+
error_line: Optional[int],
|
|
177
|
+
context_lines: int = 2,
|
|
178
|
+
) -> str:
|
|
179
|
+
"""Extract code snippet from the YAML lines around the error.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
error_line: Line number where the error occurred (1-based).
|
|
183
|
+
context_lines: Number of context lines before and after the error line.
|
|
184
|
+
Default is 2, balancing context and readability. Adjust as needed.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
A string containing the code snippet with the error highlighted.
|
|
188
|
+
"""
|
|
189
|
+
yaml_lines = self._get_serialized_yaml_lines()
|
|
190
|
+
if not yaml_lines or error_line is None:
|
|
191
|
+
return ""
|
|
192
|
+
|
|
193
|
+
start = max(error_line - context_lines - 1, 0)
|
|
194
|
+
end = min(error_line + context_lines, len(yaml_lines))
|
|
195
|
+
snippet_lines = yaml_lines[start:end]
|
|
196
|
+
snippet = ""
|
|
197
|
+
for idx, line_content in enumerate(snippet_lines, start=start + 1):
|
|
198
|
+
prefix = ">>> " if idx == error_line else " "
|
|
199
|
+
line_number_str = str(idx)
|
|
200
|
+
snippet += f"{prefix}{line_number_str} | {line_content}\n"
|
|
201
|
+
return snippet
|
|
202
|
+
|
|
203
|
+
def _get_serialized_yaml_lines(self) -> List[str]:
|
|
204
|
+
"""Serialize the content back to YAML and return the lines."""
|
|
205
|
+
yaml_lines = []
|
|
206
|
+
try:
|
|
207
|
+
yaml = YAML()
|
|
208
|
+
yaml.default_flow_style = False
|
|
209
|
+
# Set width to 1000, so we don't break the lines of the original YAML file
|
|
210
|
+
yaml.width = 1000 # type: ignore[assignment]
|
|
211
|
+
yaml.indent(mapping=2, sequence=4, offset=2)
|
|
212
|
+
stream = io.StringIO()
|
|
213
|
+
yaml.dump(self.content, stream)
|
|
214
|
+
serialized_yaml = stream.getvalue()
|
|
215
|
+
yaml_lines = serialized_yaml.splitlines()
|
|
216
|
+
return yaml_lines
|
|
217
|
+
except Exception as exc:
|
|
218
|
+
logger.debug(f"Error serializing YAML content: {exc}")
|
|
219
|
+
|
|
220
|
+
return yaml_lines
|
|
221
|
+
|
|
156
222
|
def _calculate_number_of_lines(
|
|
157
223
|
self,
|
|
158
224
|
current: Union[CommentedSeq, CommentedMap],
|
|
@@ -220,7 +286,9 @@ class YamlValidationException(YamlException, ValueError):
|
|
|
220
286
|
# Return the calculated child offset and True indicating a line number was found
|
|
221
287
|
return child_offset, True
|
|
222
288
|
|
|
223
|
-
def _line_number_for_path(
|
|
289
|
+
def _line_number_for_path(
|
|
290
|
+
self, current: Any, path: List[str], key: Optional[str] = None
|
|
291
|
+
) -> Optional[int]:
|
|
224
292
|
"""Get line number for a yaml path in the current content.
|
|
225
293
|
|
|
226
294
|
Implemented using recursion: algorithm goes down the path navigating to the
|
|
@@ -229,6 +297,7 @@ class YamlValidationException(YamlException, ValueError):
|
|
|
229
297
|
Args:
|
|
230
298
|
current: current content
|
|
231
299
|
path: path to traverse within the content
|
|
300
|
+
key: the key associated with the error, if any
|
|
232
301
|
|
|
233
302
|
Returns:
|
|
234
303
|
the line number of the path in the content.
|
|
@@ -239,6 +308,10 @@ class YamlValidationException(YamlException, ValueError):
|
|
|
239
308
|
this_line = current.lc.line + 1 if hasattr(current, "lc") else None
|
|
240
309
|
|
|
241
310
|
if not path:
|
|
311
|
+
if key and hasattr(current, "lc"):
|
|
312
|
+
if hasattr(current.lc, "data") and key in current.lc.data:
|
|
313
|
+
key_line_no = current.lc.data[key][0] + 1
|
|
314
|
+
return key_line_no
|
|
242
315
|
return this_line
|
|
243
316
|
|
|
244
317
|
head, tail = path[0], path[1:]
|
|
@@ -248,7 +321,7 @@ class YamlValidationException(YamlException, ValueError):
|
|
|
248
321
|
|
|
249
322
|
if head:
|
|
250
323
|
if isinstance(current, dict) and head in current:
|
|
251
|
-
line = self._line_number_for_path(current[head], tail)
|
|
324
|
+
line = self._line_number_for_path(current[head], tail, key)
|
|
252
325
|
if line is None:
|
|
253
326
|
line_offset, found_lc = self._calculate_number_of_lines(
|
|
254
327
|
current, head
|
|
@@ -258,14 +331,17 @@ class YamlValidationException(YamlException, ValueError):
|
|
|
258
331
|
return this_line + line_offset
|
|
259
332
|
return line
|
|
260
333
|
elif isinstance(current, list) and head.isdigit():
|
|
261
|
-
return
|
|
334
|
+
return (
|
|
335
|
+
self._line_number_for_path(current[int(head)], tail, key)
|
|
336
|
+
or this_line
|
|
337
|
+
)
|
|
262
338
|
else:
|
|
263
339
|
return this_line
|
|
264
|
-
return self._line_number_for_path(current, tail) or this_line
|
|
340
|
+
return self._line_number_for_path(current, tail, key) or this_line
|
|
265
341
|
|
|
266
342
|
|
|
267
343
|
def read_schema_file(
|
|
268
|
-
schema_file: str, package_name: str = PACKAGE_NAME
|
|
344
|
+
schema_file: str, package_name: str = PACKAGE_NAME, expand_env_vars: bool = True
|
|
269
345
|
) -> Union[List[Any], Dict[str, Any]]:
|
|
270
346
|
"""Read a schema file from the package.
|
|
271
347
|
|
|
@@ -273,12 +349,13 @@ def read_schema_file(
|
|
|
273
349
|
schema_file: The schema file to read.
|
|
274
350
|
package_name: the name of the package the schema is located in. defaults
|
|
275
351
|
to `rasa`.
|
|
352
|
+
expand_env_vars: Whether to expand environment variables in the file.
|
|
276
353
|
|
|
277
354
|
Returns:
|
|
278
355
|
The schema as a dictionary.
|
|
279
356
|
"""
|
|
280
357
|
schema_path = str(files(package_name).joinpath(schema_file))
|
|
281
|
-
return read_yaml_file(schema_path)
|
|
358
|
+
return read_yaml_file(schema_path, expand_env_vars=expand_env_vars)
|
|
282
359
|
|
|
283
360
|
|
|
284
361
|
def parse_raw_yaml(raw_yaml_content: str) -> Dict[str, Any]:
|
|
@@ -323,13 +400,26 @@ def validate_yaml_content_using_schema(
|
|
|
323
400
|
try:
|
|
324
401
|
core.validate(raise_exception=True)
|
|
325
402
|
except SchemaError:
|
|
403
|
+
# PyKwalify propagates each validation error up the data hierarchy, resulting
|
|
404
|
+
# in multiple redundant errors for a single issue. To present a clear message
|
|
405
|
+
# about the root cause, we use only the first error.
|
|
406
|
+
error = core.errors[0]
|
|
407
|
+
|
|
408
|
+
# Increment numeric indices by 1 to convert from 0-based to 1-based indexing
|
|
409
|
+
error_message = re.sub(
|
|
410
|
+
r"(/)(\d+)", lambda m: f"/{int(m.group(2)) + 1}", str(error)
|
|
411
|
+
)
|
|
412
|
+
|
|
326
413
|
raise YamlValidationException(
|
|
327
414
|
"Please make sure the file is correct and all "
|
|
328
415
|
"mandatory parameters are specified. Here are the errors "
|
|
329
416
|
"found during validation",
|
|
330
417
|
[
|
|
331
|
-
PathWithError(
|
|
332
|
-
|
|
418
|
+
PathWithError(
|
|
419
|
+
message=error_message,
|
|
420
|
+
path=error.path.removeprefix("/").split("/"),
|
|
421
|
+
key=getattr(error, "key", None),
|
|
422
|
+
)
|
|
333
423
|
],
|
|
334
424
|
content=yaml_content,
|
|
335
425
|
)
|
|
@@ -339,6 +429,7 @@ def validate_raw_yaml_using_schema(
|
|
|
339
429
|
raw_yaml_content: str,
|
|
340
430
|
schema_content: Dict[str, Any],
|
|
341
431
|
schema_extensions: Optional[List[str]] = None,
|
|
432
|
+
expand_env_vars: bool = True,
|
|
342
433
|
) -> None:
|
|
343
434
|
"""Validate raw yaml content using a schema.
|
|
344
435
|
|
|
@@ -348,6 +439,7 @@ def validate_raw_yaml_using_schema(
|
|
|
348
439
|
raw_yaml_content: the raw YAML content to be validated (usually a string)
|
|
349
440
|
schema_content: the schema for the yaml_file_content
|
|
350
441
|
schema_extensions: pykwalify schema extension files
|
|
442
|
+
expand_env_vars: Whether to expand environment variables.
|
|
351
443
|
"""
|
|
352
444
|
try:
|
|
353
445
|
# we need "rt" since
|
|
@@ -355,7 +447,11 @@ def validate_raw_yaml_using_schema(
|
|
|
355
447
|
# will include e.g. at which line an object was parsed. this is very
|
|
356
448
|
# helpful when we validate files later on and want to point the user to the
|
|
357
449
|
# right line
|
|
358
|
-
yaml_data = read_yaml(
|
|
450
|
+
yaml_data = read_yaml(
|
|
451
|
+
raw_yaml_content,
|
|
452
|
+
reader_type=["safe", "rt"],
|
|
453
|
+
expand_env_vars=expand_env_vars,
|
|
454
|
+
)
|
|
359
455
|
except (YAMLError, DuplicateKeyError) as e:
|
|
360
456
|
raise YamlSyntaxException(underlying_yaml_exception=e)
|
|
361
457
|
|
|
@@ -363,7 +459,10 @@ def validate_raw_yaml_using_schema(
|
|
|
363
459
|
|
|
364
460
|
|
|
365
461
|
def validate_raw_yaml_using_schema_file(
|
|
366
|
-
raw_yaml_content: str,
|
|
462
|
+
raw_yaml_content: str,
|
|
463
|
+
schema_path: str,
|
|
464
|
+
package_name: str = PACKAGE_NAME,
|
|
465
|
+
expand_env_vars: bool = True,
|
|
367
466
|
) -> None:
|
|
368
467
|
"""Validate raw yaml content using a schema from file.
|
|
369
468
|
|
|
@@ -372,15 +471,21 @@ def validate_raw_yaml_using_schema_file(
|
|
|
372
471
|
schema_path: the schema used for validation
|
|
373
472
|
package_name: the name of the package the schema is located in. defaults
|
|
374
473
|
to `rasa`.
|
|
474
|
+
expand_env_vars: Whether to expand environment variables in the file.
|
|
375
475
|
"""
|
|
376
|
-
schema_content = read_schema_file(
|
|
377
|
-
|
|
476
|
+
schema_content = read_schema_file(
|
|
477
|
+
schema_path, package_name, expand_env_vars=expand_env_vars
|
|
478
|
+
)
|
|
479
|
+
validate_raw_yaml_using_schema(
|
|
480
|
+
raw_yaml_content, schema_content, expand_env_vars=expand_env_vars
|
|
481
|
+
)
|
|
378
482
|
|
|
379
483
|
|
|
380
484
|
def validate_raw_yaml_content_using_schema_with_responses(
|
|
381
485
|
raw_yaml_content: str,
|
|
382
486
|
schema_content: Union[List[Any], Dict[str, Any]],
|
|
383
487
|
package_name: str = PACKAGE_NAME,
|
|
488
|
+
expand_env_vars: bool = True,
|
|
384
489
|
) -> None:
|
|
385
490
|
"""Validate raw yaml content using a schema with responses sub-schema.
|
|
386
491
|
|
|
@@ -389,18 +494,26 @@ def validate_raw_yaml_content_using_schema_with_responses(
|
|
|
389
494
|
schema_content: the content of the YAML schema
|
|
390
495
|
package_name: the name of the package the schema is located in. defaults
|
|
391
496
|
to `rasa`.
|
|
497
|
+
expand_env_vars: Whether to expand environment variables in the file.
|
|
392
498
|
"""
|
|
393
499
|
# bot responses are part of the schema extension
|
|
394
500
|
# it will be included if the schema explicitly references it with include: responses
|
|
395
|
-
bot_responses_schema_content = read_schema_file(
|
|
501
|
+
bot_responses_schema_content = read_schema_file(
|
|
502
|
+
RESPONSES_SCHEMA_FILE, package_name, expand_env_vars=expand_env_vars
|
|
503
|
+
)
|
|
396
504
|
schema_content = dict(schema_content, **bot_responses_schema_content)
|
|
397
505
|
schema_extensions = [str(files(package_name).joinpath(SCHEMA_EXTENSIONS_FILE))]
|
|
398
506
|
|
|
399
|
-
validate_raw_yaml_using_schema(
|
|
507
|
+
validate_raw_yaml_using_schema(
|
|
508
|
+
raw_yaml_content, schema_content, schema_extensions, expand_env_vars
|
|
509
|
+
)
|
|
400
510
|
|
|
401
511
|
|
|
402
512
|
def validate_raw_yaml_using_schema_file_with_responses(
|
|
403
|
-
raw_yaml_content: str,
|
|
513
|
+
raw_yaml_content: str,
|
|
514
|
+
schema_path: str,
|
|
515
|
+
package_name: str = PACKAGE_NAME,
|
|
516
|
+
expand_env_vars: bool = True,
|
|
404
517
|
) -> None:
|
|
405
518
|
"""Validate domain yaml content using a schema from file with responses sub-schema.
|
|
406
519
|
|
|
@@ -409,10 +522,11 @@ def validate_raw_yaml_using_schema_file_with_responses(
|
|
|
409
522
|
schema_path: the schema of the yaml file
|
|
410
523
|
package_name: the name of the package the schema is located in. defaults
|
|
411
524
|
to `rasa`.
|
|
525
|
+
expand_env_vars: Whether to expand environment variables in the file.
|
|
412
526
|
"""
|
|
413
|
-
schema_content = read_schema_file(schema_path, package_name)
|
|
527
|
+
schema_content = read_schema_file(schema_path, package_name, expand_env_vars)
|
|
414
528
|
validate_raw_yaml_content_using_schema_with_responses(
|
|
415
|
-
raw_yaml_content, schema_content, package_name
|
|
529
|
+
raw_yaml_content, schema_content, package_name, expand_env_vars
|
|
416
530
|
)
|
|
417
531
|
|
|
418
532
|
|
|
@@ -432,11 +546,14 @@ def read_yaml(
|
|
|
432
546
|
ruamel.yaml.parser.ParserError: If there was an error when parsing the YAML.
|
|
433
547
|
"""
|
|
434
548
|
custom_constructor = kwargs.get("custom_constructor", None)
|
|
549
|
+
expand_env_vars = kwargs.get("expand_env_vars", True)
|
|
435
550
|
|
|
436
551
|
# Create YAML parser with custom constructor
|
|
437
552
|
yaml_parser, reset_constructors = create_yaml_parser(
|
|
438
553
|
reader_type, custom_constructor
|
|
439
554
|
)
|
|
555
|
+
if expand_env_vars:
|
|
556
|
+
replace_environment_variables()
|
|
440
557
|
yaml_content = yaml_parser.load(content) or {}
|
|
441
558
|
|
|
442
559
|
# Reset to default constructors
|
|
@@ -492,6 +609,10 @@ def create_yaml_parser(
|
|
|
492
609
|
yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG,
|
|
493
610
|
original_sequence_constructor,
|
|
494
611
|
)
|
|
612
|
+
# replace env var constructor with one that does not expand env vars
|
|
613
|
+
yaml_parser.constructor.add_constructor(
|
|
614
|
+
"!env_var", lambda loader, node: loader.construct_scalar(node)
|
|
615
|
+
)
|
|
495
616
|
|
|
496
617
|
def custom_date_constructor(loader: SafeLoader, node: ScalarNode) -> str:
|
|
497
618
|
"""Custom constructor for parsing dates in the format '%Y-%m-%d'.
|
|
@@ -526,7 +647,9 @@ def _is_ascii(text: str) -> bool:
|
|
|
526
647
|
|
|
527
648
|
@lru_cache(maxsize=READ_YAML_FILE_CACHE_MAXSIZE)
|
|
528
649
|
def read_yaml_file(
|
|
529
|
-
filename: Union[str, Path],
|
|
650
|
+
filename: Union[str, Path],
|
|
651
|
+
reader_type: Union[str, Tuple[str]] = "safe",
|
|
652
|
+
expand_env_vars: bool = True,
|
|
530
653
|
) -> Union[List[Any], Dict[str, Any]]:
|
|
531
654
|
"""Parses a yaml file.
|
|
532
655
|
|
|
@@ -535,6 +658,7 @@ def read_yaml_file(
|
|
|
535
658
|
Args:
|
|
536
659
|
filename: The path to the file which should be read.
|
|
537
660
|
reader_type: Reader type to use. By default "safe" will be used.
|
|
661
|
+
expand_env_vars: Whether to expand environment variables in the file.
|
|
538
662
|
|
|
539
663
|
Returns:
|
|
540
664
|
Parsed content of the file.
|
|
@@ -543,7 +667,11 @@ def read_yaml_file(
|
|
|
543
667
|
fixed_reader_type = (
|
|
544
668
|
list(reader_type) if isinstance(reader_type, tuple) else reader_type
|
|
545
669
|
)
|
|
546
|
-
return read_yaml(
|
|
670
|
+
return read_yaml(
|
|
671
|
+
read_file(filename, DEFAULT_ENCODING),
|
|
672
|
+
fixed_reader_type,
|
|
673
|
+
expand_env_vars=expand_env_vars,
|
|
674
|
+
)
|
|
547
675
|
except (YAMLError, DuplicateKeyError) as e:
|
|
548
676
|
raise YamlSyntaxException(filename, e)
|
|
549
677
|
|
|
@@ -566,11 +694,14 @@ def read_config_file(
|
|
|
566
694
|
return read_validated_yaml(filename, CONFIG_SCHEMA_FILE, reader_type)
|
|
567
695
|
|
|
568
696
|
|
|
569
|
-
def read_model_configuration(
|
|
697
|
+
def read_model_configuration(
|
|
698
|
+
filename: Union[Path, str], expand_env_vars: bool = True
|
|
699
|
+
) -> Dict[str, Any]:
|
|
570
700
|
"""Parses a model configuration file.
|
|
571
701
|
|
|
572
702
|
Args:
|
|
573
703
|
filename: The path to the file which should be read.
|
|
704
|
+
expand_env_vars: Whether to expand environment variables in the file.
|
|
574
705
|
|
|
575
706
|
Raises:
|
|
576
707
|
YamlValidationException: In case the model configuration doesn't match the
|
|
@@ -579,7 +710,9 @@ def read_model_configuration(filename: Union[Path, str]) -> Dict[str, Any]:
|
|
|
579
710
|
Returns:
|
|
580
711
|
Parsed config file.
|
|
581
712
|
"""
|
|
582
|
-
return read_validated_yaml(
|
|
713
|
+
return read_validated_yaml(
|
|
714
|
+
filename, MODEL_CONFIG_SCHEMA_FILE, expand_env_vars=expand_env_vars
|
|
715
|
+
)
|
|
583
716
|
|
|
584
717
|
|
|
585
718
|
def dump_obj_as_yaml_to_string(
|
|
@@ -677,6 +810,7 @@ def read_validated_yaml(
|
|
|
677
810
|
filename: Union[str, Path],
|
|
678
811
|
schema: str,
|
|
679
812
|
reader_type: Union[str, List[str]] = "safe",
|
|
813
|
+
expand_env_vars: bool = True,
|
|
680
814
|
) -> Any:
|
|
681
815
|
"""Validates YAML file content and returns parsed content.
|
|
682
816
|
|
|
@@ -685,6 +819,7 @@ def read_validated_yaml(
|
|
|
685
819
|
schema: The path to the schema file which should be used for validating the
|
|
686
820
|
file content.
|
|
687
821
|
reader_type: Reader type to use. By default, "safe" will be used.
|
|
822
|
+
expand_env_vars: Whether to expand environment variables in the file.
|
|
688
823
|
|
|
689
824
|
Returns:
|
|
690
825
|
The parsed file content.
|
|
@@ -695,8 +830,10 @@ def read_validated_yaml(
|
|
|
695
830
|
"""
|
|
696
831
|
content = read_file(filename)
|
|
697
832
|
|
|
698
|
-
validate_raw_yaml_using_schema_file(
|
|
699
|
-
|
|
833
|
+
validate_raw_yaml_using_schema_file(
|
|
834
|
+
content, schema, expand_env_vars=expand_env_vars
|
|
835
|
+
)
|
|
836
|
+
return read_yaml(content, reader_type, expand_env_vars=expand_env_vars)
|
|
700
837
|
|
|
701
838
|
|
|
702
839
|
def validate_training_data(json_data: Dict[str, Any], schema: Dict[str, Any]) -> None:
|
|
@@ -815,6 +952,7 @@ def validate_yaml_with_jsonschema(
|
|
|
815
952
|
humanize_error: Callable[
|
|
816
953
|
[jsonschema.ValidationError], str
|
|
817
954
|
] = default_error_humanizer,
|
|
955
|
+
expand_env_vars: bool = True,
|
|
818
956
|
) -> None:
|
|
819
957
|
"""Validate data format.
|
|
820
958
|
|
|
@@ -825,6 +963,7 @@ def validate_yaml_with_jsonschema(
|
|
|
825
963
|
to `rasa`.
|
|
826
964
|
humanize_error: a function to convert a jsonschema.ValidationError into a
|
|
827
965
|
human-readable error message. Defaults to `default_error_humanizer`.
|
|
966
|
+
expand_env_vars: Whether to expand environment variables in the file.
|
|
828
967
|
|
|
829
968
|
Raises:
|
|
830
969
|
YamlSyntaxException: if the yaml file is not valid.
|
|
@@ -842,7 +981,11 @@ def validate_yaml_with_jsonschema(
|
|
|
842
981
|
# will include e.g. at which line an object was parsed. this is very
|
|
843
982
|
# helpful when we validate files later on and want to point the user to the
|
|
844
983
|
# right line
|
|
845
|
-
source_data = read_yaml(
|
|
984
|
+
source_data = read_yaml(
|
|
985
|
+
yaml_file_content,
|
|
986
|
+
reader_type=["safe", "rt"],
|
|
987
|
+
expand_env_vars=expand_env_vars,
|
|
988
|
+
)
|
|
846
989
|
except (YAMLError, DuplicateKeyError) as e:
|
|
847
990
|
raise YamlSyntaxException(underlying_yaml_exception=e)
|
|
848
991
|
|
rasa/studio/data_handler.py
CHANGED
|
@@ -360,7 +360,9 @@ def import_data_from_studio(
|
|
|
360
360
|
studio_domain.persist(domain_file)
|
|
361
361
|
|
|
362
362
|
data_from_studio = TrainingDataImporter.load_from_dict(
|
|
363
|
-
domain_path=str(domain_file),
|
|
363
|
+
domain_path=str(domain_file),
|
|
364
|
+
training_data_paths=data_paths,
|
|
365
|
+
expand_env_vars=False,
|
|
364
366
|
)
|
|
365
367
|
|
|
366
368
|
return data_from_studio, data_original
|