rasa-pro 3.11.0a4.dev3__py3-none-any.whl → 3.11.0rc2__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 +22 -12
- rasa/api.py +1 -1
- rasa/cli/arguments/default_arguments.py +1 -2
- rasa/cli/arguments/shell.py +5 -1
- rasa/cli/e2e_test.py +1 -1
- rasa/cli/evaluate.py +8 -8
- rasa/cli/inspect.py +6 -4
- rasa/cli/llm_fine_tuning.py +1 -1
- rasa/cli/project_templates/calm/config.yml +5 -7
- rasa/cli/project_templates/calm/endpoints.yml +8 -0
- 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 +7 -7
- rasa/cli/run.py +1 -1
- rasa/cli/scaffold.py +4 -2
- rasa/cli/studio/studio.py +18 -8
- rasa/cli/utils.py +5 -0
- rasa/cli/x.py +8 -8
- rasa/constants.py +1 -1
- rasa/core/actions/action_repeat_bot_messages.py +17 -0
- rasa/core/channels/channel.py +20 -0
- rasa/core/channels/inspector/dist/assets/{arc-6852c607.js → arc-bc141fb2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-d0fbc5ce-acc952b2.js → c4Diagram-d0fbc5ce-be2db283.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-936ed81e-848a7597.js → classDiagram-936ed81e-55366915.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-c3cb15f1-a73d3e68.js → classDiagram-v2-c3cb15f1-bb529518.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{createText-62fc7601-e5ee049d.js → createText-62fc7601-b0ec81d6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-f2ad444c-771e517e.js → edges-f2ad444c-6166330c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9d236eb7-aa347178.js → erDiagram-9d236eb7-5ccc6a8e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-1972c806-651fc57d.js → flowDb-1972c806-fca3bfe4.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-7ea5b25a-ca67804f.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-2dbc568d.js → flowchart-elk-definition-abe16c3d-7c1b0e0f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-9b5ea136-25a65bd8.js → ganttDiagram-9b5ea136-772fd050.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-99d0ae7c-fdc7378d.js → gitGraphDiagram-99d0ae7c-8eae1dc9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-2c4b9a3b-6f1fd606.js → index-2c4b9a3b-f55afcdf.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-efdd30c1.js → index-e7cef9de.js} +68 -68
- rasa/core/channels/inspector/dist/assets/{infoDiagram-736b4530-cb1a041a.js → infoDiagram-736b4530-124d4a14.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-df861f2b-14609879.js → journeyDiagram-df861f2b-7c4fae44.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-2490f52b.js → layout-b9885fb6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-40186f1f.js → line-7c59abb6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-08814e93.js → linear-4776f780.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-beec6740-1a534584.js → mindmap-definition-beec6740-2332c46c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-dbbf0591-72397b61.js → pieDiagram-dbbf0591-8fb39303.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-4d7f4fd6-3bb0b6a3.js → quadrantDiagram-4d7f4fd6-3c7180a2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-6fc4c22a-57334f61.js → requirementDiagram-6fc4c22a-e910bcb8.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-8f13d901-111e1297.js → sankeyDiagram-8f13d901-ead16c89.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-b655622a-10bcfe62.js → sequenceDiagram-b655622a-29a02a19.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-59f0c015-acaf7513.js → stateDiagram-59f0c015-042b3137.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-2b26beab-3ec2a235.js → stateDiagram-v2-2b26beab-2178c0f3.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-080da4f6-62730289.js → styles-080da4f6-23ffa4fc.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-3dcbcfbf-5284ee76.js → styles-3dcbcfbf-94f59763.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9c745c82-642435e3.js → styles-9c745c82-78a6bebc.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-4835440b-b250a350.js → svgDrawCommon-4835440b-eae2a6f6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-5b62e21b-c2b147ed.js → timeline-definition-5b62e21b-5c968d92.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-2b33534f-f92cfea9.js → xychartDiagram-2b33534f-fd3db0d5.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +1 -1
- rasa/core/channels/inspector/src/App.tsx +1 -1
- rasa/core/channels/inspector/src/helpers/audiostream.ts +77 -16
- rasa/core/channels/socketio.py +2 -1
- rasa/core/channels/telegram.py +1 -1
- rasa/core/channels/twilio.py +1 -1
- rasa/core/channels/voice_ready/audiocodes.py +12 -0
- rasa/core/channels/voice_ready/jambonz.py +15 -4
- rasa/core/channels/voice_ready/twilio_voice.py +6 -21
- rasa/core/channels/voice_stream/asr/asr_event.py +5 -0
- rasa/core/channels/voice_stream/asr/azure.py +122 -0
- rasa/core/channels/voice_stream/asr/deepgram.py +16 -6
- rasa/core/channels/voice_stream/audio_bytes.py +1 -0
- rasa/core/channels/voice_stream/browser_audio.py +31 -8
- rasa/core/channels/voice_stream/call_state.py +23 -0
- rasa/core/channels/voice_stream/tts/azure.py +6 -2
- rasa/core/channels/voice_stream/tts/cartesia.py +10 -6
- rasa/core/channels/voice_stream/tts/tts_engine.py +1 -0
- rasa/core/channels/voice_stream/twilio_media_streams.py +27 -18
- rasa/core/channels/voice_stream/util.py +4 -4
- rasa/core/channels/voice_stream/voice_channel.py +189 -39
- rasa/core/featurizers/single_state_featurizer.py +22 -1
- rasa/core/featurizers/tracker_featurizers.py +115 -18
- rasa/core/nlg/contextual_response_rephraser.py +32 -30
- rasa/core/persistor.py +86 -39
- rasa/core/policies/enterprise_search_policy.py +119 -60
- rasa/core/policies/flows/flow_executor.py +7 -4
- rasa/core/policies/intentless_policy.py +78 -22
- rasa/core/policies/ted_policy.py +58 -33
- rasa/core/policies/unexpected_intent_policy.py +15 -7
- rasa/core/processor.py +25 -0
- rasa/core/training/interactive.py +34 -35
- rasa/core/utils.py +8 -3
- rasa/dialogue_understanding/coexistence/llm_based_router.py +39 -12
- rasa/dialogue_understanding/commands/change_flow_command.py +6 -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 +49 -4
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +37 -23
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +57 -10
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +19 -1
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +71 -11
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +39 -0
- rasa/dialogue_understanding/patterns/user_silence.py +37 -0
- rasa/dialogue_understanding/processor/command_processor.py +21 -1
- rasa/e2e_test/e2e_test_case.py +85 -6
- rasa/e2e_test/e2e_test_runner.py +4 -2
- rasa/e2e_test/utils/io.py +1 -1
- rasa/engine/validation.py +316 -10
- rasa/model_manager/config.py +15 -3
- rasa/model_manager/model_api.py +15 -7
- rasa/model_manager/runner_service.py +8 -6
- rasa/model_manager/socket_bridge.py +6 -3
- rasa/model_manager/trainer_service.py +7 -5
- rasa/model_manager/utils.py +28 -7
- rasa/model_service.py +9 -2
- rasa/model_training.py +2 -0
- rasa/nlu/classifiers/diet_classifier.py +38 -25
- rasa/nlu/classifiers/logistic_regression_classifier.py +22 -9
- rasa/nlu/classifiers/sklearn_intent_classifier.py +37 -16
- rasa/nlu/extractors/crf_entity_extractor.py +93 -50
- rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +45 -16
- rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +52 -17
- rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +5 -3
- rasa/nlu/tokenizers/whitespace_tokenizer.py +3 -14
- rasa/server.py +3 -1
- rasa/shared/constants.py +36 -3
- rasa/shared/core/constants.py +7 -0
- rasa/shared/core/domain.py +26 -0
- rasa/shared/core/flows/flow.py +5 -0
- rasa/shared/core/flows/flows_list.py +5 -1
- rasa/shared/core/flows/flows_yaml_schema.json +10 -0
- rasa/shared/core/flows/utils.py +39 -0
- rasa/shared/core/flows/validation.py +96 -0
- rasa/shared/core/slots.py +5 -0
- rasa/shared/nlu/training_data/features.py +120 -2
- rasa/shared/providers/_configs/azure_openai_client_config.py +5 -3
- rasa/shared/providers/_configs/litellm_router_client_config.py +200 -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/embedding/_base_litellm_embedding_client.py +18 -29
- rasa/shared/providers/embedding/azure_openai_embedding_client.py +54 -21
- rasa/shared/providers/embedding/litellm_router_embedding_client.py +135 -0
- rasa/shared/providers/llm/_base_litellm_client.py +37 -31
- rasa/shared/providers/llm/azure_openai_llm_client.py +50 -29
- rasa/shared/providers/llm/litellm_router_llm_client.py +127 -0
- rasa/shared/providers/llm/rasa_llm_client.py +112 -0
- rasa/shared/providers/llm/self_hosted_llm_client.py +1 -1
- rasa/shared/providers/mappings.py +19 -0
- rasa/shared/providers/router/__init__.py +0 -0
- rasa/shared/providers/router/_base_litellm_router_client.py +149 -0
- rasa/shared/providers/router/router_client.py +73 -0
- rasa/shared/utils/common.py +8 -0
- 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 +256 -0
- rasa/shared/utils/health_check/llm_health_check_mixin.py +31 -0
- rasa/shared/utils/io.py +28 -6
- rasa/shared/utils/llm.py +353 -46
- rasa/shared/utils/yaml.py +111 -73
- rasa/studio/auth.py +3 -5
- rasa/studio/config.py +13 -4
- rasa/studio/constants.py +1 -0
- rasa/studio/data_handler.py +10 -3
- rasa/studio/upload.py +81 -26
- rasa/telemetry.py +92 -17
- rasa/tracing/config.py +2 -0
- rasa/tracing/instrumentation/attribute_extractors.py +94 -17
- rasa/tracing/instrumentation/instrumentation.py +121 -0
- rasa/utils/common.py +5 -0
- rasa/utils/io.py +7 -81
- rasa/utils/log_utils.py +9 -2
- rasa/utils/sanic_error_handler.py +32 -0
- rasa/utils/tensorflow/feature_array.py +366 -0
- rasa/utils/tensorflow/model_data.py +2 -193
- rasa/validator.py +70 -0
- rasa/version.py +1 -1
- {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc2.dist-info}/METADATA +11 -10
- {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc2.dist-info}/RECORD +183 -163
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-587d82d8.js +0 -1
- {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc2.dist-info}/NOTICE +0 -0
- {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc2.dist-info}/WHEEL +0 -0
- {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc2.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,8 +26,7 @@ from rasa.shared.constants import (
|
|
|
31
26
|
LATEST_TRAINING_DATA_FORMAT_VERSION,
|
|
32
27
|
SCHEMA_EXTENSIONS_FILE,
|
|
33
28
|
RESPONSES_SCHEMA_FILE,
|
|
34
|
-
|
|
35
|
-
RESOLVED_VALUE,
|
|
29
|
+
API_KEY,
|
|
36
30
|
)
|
|
37
31
|
from rasa.shared.exceptions import (
|
|
38
32
|
YamlException,
|
|
@@ -52,6 +46,11 @@ from rasa.shared.utils.io import (
|
|
|
52
46
|
raise_warning,
|
|
53
47
|
read_json_file,
|
|
54
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
|
|
55
54
|
|
|
56
55
|
logger = logging.getLogger(__name__)
|
|
57
56
|
|
|
@@ -60,12 +59,22 @@ YAML_VERSION = (1, 2)
|
|
|
60
59
|
READ_YAML_FILE_CACHE_MAXSIZE = os.environ.get(
|
|
61
60
|
READ_YAML_FILE_CACHE_MAXSIZE_ENV_VAR, DEFAULT_READ_YAML_FILE_CACHE_MAXSIZE
|
|
62
61
|
)
|
|
62
|
+
SENSITIVE_DATA = [API_KEY]
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
@dataclass
|
|
66
66
|
class PathWithError:
|
|
67
|
+
"""Represents a validation error at a specific location in the YAML content.
|
|
68
|
+
|
|
69
|
+
Attributes:
|
|
70
|
+
message (str): A description of the validation error.
|
|
71
|
+
path (List[str]): Path to the node where the error occurred.
|
|
72
|
+
key (Optional[str]): The specific key associated with the error, if any.
|
|
73
|
+
"""
|
|
74
|
+
|
|
67
75
|
message: str
|
|
68
76
|
path: List[str] = field(default_factory=list)
|
|
77
|
+
key: Optional[str] = None
|
|
69
78
|
|
|
70
79
|
|
|
71
80
|
def fix_yaml_loader() -> None:
|
|
@@ -86,11 +95,15 @@ def replace_environment_variables() -> None:
|
|
|
86
95
|
env_var_pattern = re.compile(r"^(.*)\$\{(.*)\}(.*)$")
|
|
87
96
|
yaml.Resolver.add_implicit_resolver("!env_var", env_var_pattern, None)
|
|
88
97
|
|
|
89
|
-
def env_var_constructor(
|
|
90
|
-
loader: BaseConstructor, node: ScalarNode
|
|
91
|
-
) -> Union[dict, str]:
|
|
98
|
+
def env_var_constructor(loader: BaseConstructor, node: ScalarNode) -> str:
|
|
92
99
|
"""Process environment variables found in the YAML."""
|
|
93
100
|
value = loader.construct_scalar(node)
|
|
101
|
+
|
|
102
|
+
# get key of current node
|
|
103
|
+
key_node = list(loader.constructed_objects)[-1]
|
|
104
|
+
if isinstance(key_node, ScalarNode) and key_node.value in SENSITIVE_DATA:
|
|
105
|
+
return value
|
|
106
|
+
|
|
94
107
|
expanded_vars = os.path.expandvars(value)
|
|
95
108
|
not_expanded = [
|
|
96
109
|
w for w in expanded_vars.split() if w.startswith("$") and w in value
|
|
@@ -102,11 +115,6 @@ def replace_environment_variables() -> None:
|
|
|
102
115
|
f"Please make sure to also set these "
|
|
103
116
|
f"environment variables: '{not_expanded}'."
|
|
104
117
|
)
|
|
105
|
-
if expanded_vars:
|
|
106
|
-
# if the environment variable is referenced using the ${} syntax
|
|
107
|
-
# then we return a dictionary with the original value and the resolved,
|
|
108
|
-
# value. So that the graph components can use the original value.
|
|
109
|
-
return {ORIGINAL_VALUE: value, RESOLVED_VALUE: expanded_vars}
|
|
110
118
|
return expanded_vars
|
|
111
119
|
|
|
112
120
|
yaml.SafeConstructor.add_constructor("!env_var", env_var_constructor)
|
|
@@ -147,21 +155,72 @@ class YamlValidationException(YamlException, ValueError):
|
|
|
147
155
|
if self.validation_errors:
|
|
148
156
|
unique_errors = {}
|
|
149
157
|
for error in self.validation_errors:
|
|
150
|
-
line_number = self._line_number_for_path(
|
|
158
|
+
line_number = self._line_number_for_path(
|
|
159
|
+
self.content, error.path, error.key
|
|
160
|
+
)
|
|
151
161
|
|
|
152
162
|
if line_number and self.filename:
|
|
153
|
-
|
|
163
|
+
error_location = f" in {self.filename}:{line_number}:\n"
|
|
154
164
|
elif line_number:
|
|
155
|
-
|
|
165
|
+
error_location = f" in Line {line_number}:\n"
|
|
156
166
|
else:
|
|
157
|
-
|
|
167
|
+
error_location = ""
|
|
158
168
|
|
|
159
|
-
|
|
160
|
-
|
|
169
|
+
code_snippet = self._get_code_snippet(line_number)
|
|
170
|
+
error_message = f"{error_location}\n{code_snippet}{error.message}\n"
|
|
171
|
+
unique_errors[error.message] = error_message
|
|
161
172
|
error_msg = "\n".join(unique_errors.values())
|
|
162
173
|
msg += f":\n{error_msg}"
|
|
163
174
|
return msg
|
|
164
175
|
|
|
176
|
+
def _get_code_snippet(
|
|
177
|
+
self,
|
|
178
|
+
error_line: Optional[int],
|
|
179
|
+
context_lines: int = 2,
|
|
180
|
+
) -> str:
|
|
181
|
+
"""Extract code snippet from the YAML lines around the error.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
error_line: Line number where the error occurred (1-based).
|
|
185
|
+
context_lines: Number of context lines before and after the error line.
|
|
186
|
+
Default is 2, balancing context and readability. Adjust as needed.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
A string containing the code snippet with the error highlighted.
|
|
190
|
+
"""
|
|
191
|
+
yaml_lines = self._get_serialized_yaml_lines()
|
|
192
|
+
if not yaml_lines or error_line is None:
|
|
193
|
+
return ""
|
|
194
|
+
|
|
195
|
+
start = max(error_line - context_lines - 1, 0)
|
|
196
|
+
end = min(error_line + context_lines, len(yaml_lines))
|
|
197
|
+
snippet_lines = yaml_lines[start:end]
|
|
198
|
+
snippet = ""
|
|
199
|
+
for idx, line_content in enumerate(snippet_lines, start=start + 1):
|
|
200
|
+
prefix = ">>> " if idx == error_line else " "
|
|
201
|
+
line_number_str = str(idx)
|
|
202
|
+
snippet += f"{prefix}{line_number_str} | {line_content}\n"
|
|
203
|
+
return snippet
|
|
204
|
+
|
|
205
|
+
def _get_serialized_yaml_lines(self) -> List[str]:
|
|
206
|
+
"""Serialize the content back to YAML and return the lines."""
|
|
207
|
+
yaml_lines = []
|
|
208
|
+
try:
|
|
209
|
+
yaml = YAML()
|
|
210
|
+
yaml.default_flow_style = False
|
|
211
|
+
# Set width to 1000, so we don't break the lines of the original YAML file
|
|
212
|
+
yaml.width = 1000 # type: ignore[assignment]
|
|
213
|
+
yaml.indent(mapping=2, sequence=4, offset=2)
|
|
214
|
+
stream = io.StringIO()
|
|
215
|
+
yaml.dump(self.content, stream)
|
|
216
|
+
serialized_yaml = stream.getvalue()
|
|
217
|
+
yaml_lines = serialized_yaml.splitlines()
|
|
218
|
+
return yaml_lines
|
|
219
|
+
except Exception as exc:
|
|
220
|
+
logger.debug(f"Error serializing YAML content: {exc}")
|
|
221
|
+
|
|
222
|
+
return yaml_lines
|
|
223
|
+
|
|
165
224
|
def _calculate_number_of_lines(
|
|
166
225
|
self,
|
|
167
226
|
current: Union[CommentedSeq, CommentedMap],
|
|
@@ -229,7 +288,9 @@ class YamlValidationException(YamlException, ValueError):
|
|
|
229
288
|
# Return the calculated child offset and True indicating a line number was found
|
|
230
289
|
return child_offset, True
|
|
231
290
|
|
|
232
|
-
def _line_number_for_path(
|
|
291
|
+
def _line_number_for_path(
|
|
292
|
+
self, current: Any, path: List[str], key: Optional[str] = None
|
|
293
|
+
) -> Optional[int]:
|
|
233
294
|
"""Get line number for a yaml path in the current content.
|
|
234
295
|
|
|
235
296
|
Implemented using recursion: algorithm goes down the path navigating to the
|
|
@@ -238,6 +299,7 @@ class YamlValidationException(YamlException, ValueError):
|
|
|
238
299
|
Args:
|
|
239
300
|
current: current content
|
|
240
301
|
path: path to traverse within the content
|
|
302
|
+
key: the key associated with the error, if any
|
|
241
303
|
|
|
242
304
|
Returns:
|
|
243
305
|
the line number of the path in the content.
|
|
@@ -248,6 +310,10 @@ class YamlValidationException(YamlException, ValueError):
|
|
|
248
310
|
this_line = current.lc.line + 1 if hasattr(current, "lc") else None
|
|
249
311
|
|
|
250
312
|
if not path:
|
|
313
|
+
if key and hasattr(current, "lc"):
|
|
314
|
+
if hasattr(current.lc, "data") and key in current.lc.data:
|
|
315
|
+
key_line_no = current.lc.data[key][0] + 1
|
|
316
|
+
return key_line_no
|
|
251
317
|
return this_line
|
|
252
318
|
|
|
253
319
|
head, tail = path[0], path[1:]
|
|
@@ -257,7 +323,7 @@ class YamlValidationException(YamlException, ValueError):
|
|
|
257
323
|
|
|
258
324
|
if head:
|
|
259
325
|
if isinstance(current, dict) and head in current:
|
|
260
|
-
line = self._line_number_for_path(current[head], tail)
|
|
326
|
+
line = self._line_number_for_path(current[head], tail, key)
|
|
261
327
|
if line is None:
|
|
262
328
|
line_offset, found_lc = self._calculate_number_of_lines(
|
|
263
329
|
current, head
|
|
@@ -267,10 +333,13 @@ class YamlValidationException(YamlException, ValueError):
|
|
|
267
333
|
return this_line + line_offset
|
|
268
334
|
return line
|
|
269
335
|
elif isinstance(current, list) and head.isdigit():
|
|
270
|
-
return
|
|
336
|
+
return (
|
|
337
|
+
self._line_number_for_path(current[int(head)], tail, key)
|
|
338
|
+
or this_line
|
|
339
|
+
)
|
|
271
340
|
else:
|
|
272
341
|
return this_line
|
|
273
|
-
return self._line_number_for_path(current, tail) or this_line
|
|
342
|
+
return self._line_number_for_path(current, tail, key) or this_line
|
|
274
343
|
|
|
275
344
|
|
|
276
345
|
def read_schema_file(
|
|
@@ -332,13 +401,26 @@ def validate_yaml_content_using_schema(
|
|
|
332
401
|
try:
|
|
333
402
|
core.validate(raise_exception=True)
|
|
334
403
|
except SchemaError:
|
|
404
|
+
# PyKwalify propagates each validation error up the data hierarchy, resulting
|
|
405
|
+
# in multiple redundant errors for a single issue. To present a clear message
|
|
406
|
+
# about the root cause, we use only the first error.
|
|
407
|
+
error = core.errors[0]
|
|
408
|
+
|
|
409
|
+
# Increment numeric indices by 1 to convert from 0-based to 1-based indexing
|
|
410
|
+
error_message = re.sub(
|
|
411
|
+
r"(/)(\d+)", lambda m: f"/{int(m.group(2)) + 1}", str(error)
|
|
412
|
+
)
|
|
413
|
+
|
|
335
414
|
raise YamlValidationException(
|
|
336
415
|
"Please make sure the file is correct and all "
|
|
337
416
|
"mandatory parameters are specified. Here are the errors "
|
|
338
417
|
"found during validation",
|
|
339
418
|
[
|
|
340
|
-
PathWithError(
|
|
341
|
-
|
|
419
|
+
PathWithError(
|
|
420
|
+
message=error_message,
|
|
421
|
+
path=error.path.removeprefix("/").split("/"),
|
|
422
|
+
key=getattr(error, "key", None),
|
|
423
|
+
)
|
|
342
424
|
],
|
|
343
425
|
content=yaml_content,
|
|
344
426
|
)
|
|
@@ -425,47 +507,6 @@ def validate_raw_yaml_using_schema_file_with_responses(
|
|
|
425
507
|
)
|
|
426
508
|
|
|
427
509
|
|
|
428
|
-
def process_content(content: str) -> str:
|
|
429
|
-
"""
|
|
430
|
-
Process the content to handle both Windows paths and emojis.
|
|
431
|
-
Windows paths are processed by escaping backslashes but emojis are left untouched.
|
|
432
|
-
|
|
433
|
-
Args:
|
|
434
|
-
content: yaml content to be processed
|
|
435
|
-
"""
|
|
436
|
-
# Detect common Windows path patterns: e.g., C:\ or \\
|
|
437
|
-
UNESCAPED_WINDOWS_PATH_PATTERN = re.compile(
|
|
438
|
-
r"(?<!\w)[a-zA-Z]:(\\[a-zA-Z0-9_ -]+)*(\\)?(?!\\n)"
|
|
439
|
-
)
|
|
440
|
-
ESCAPED_WINDOWS_PATH_PATTERN = re.compile(
|
|
441
|
-
r"(?<!\w)[a-zA-Z]:(\\\\[a-zA-Z0-9_ -]+)+\\\\?(?!\\n)"
|
|
442
|
-
)
|
|
443
|
-
|
|
444
|
-
# Function to escape backslashes in Windows paths but leave other content as is
|
|
445
|
-
def escape_windows_paths(match: re.Match) -> str:
|
|
446
|
-
path = str(match.group(0))
|
|
447
|
-
return path.replace("\\", "\\\\") # Escape backslashes only in Windows paths
|
|
448
|
-
|
|
449
|
-
def unescape_windows_paths(match: re.Match) -> str:
|
|
450
|
-
path = str(match.group(0))
|
|
451
|
-
return path.replace("\\\\", "\\")
|
|
452
|
-
|
|
453
|
-
# First, process Windows paths by escaping backslashes
|
|
454
|
-
content = re.sub(UNESCAPED_WINDOWS_PATH_PATTERN, escape_windows_paths, content)
|
|
455
|
-
|
|
456
|
-
# Ensure proper handling of emojis by decoding Unicode sequences
|
|
457
|
-
content = (
|
|
458
|
-
content.encode("utf-8")
|
|
459
|
-
.decode("raw_unicode_escape")
|
|
460
|
-
.encode("utf-16", "surrogatepass")
|
|
461
|
-
.decode("utf-16")
|
|
462
|
-
)
|
|
463
|
-
|
|
464
|
-
content = re.sub(ESCAPED_WINDOWS_PATH_PATTERN, unescape_windows_paths, content)
|
|
465
|
-
|
|
466
|
-
return content
|
|
467
|
-
|
|
468
|
-
|
|
469
510
|
def read_yaml(
|
|
470
511
|
content: str,
|
|
471
512
|
reader_type: Union[str, List[str]] = "safe",
|
|
@@ -481,9 +522,6 @@ def read_yaml(
|
|
|
481
522
|
Raises:
|
|
482
523
|
ruamel.yaml.parser.ParserError: If there was an error when parsing the YAML.
|
|
483
524
|
"""
|
|
484
|
-
if _is_ascii(content):
|
|
485
|
-
content = process_content(content)
|
|
486
|
-
|
|
487
525
|
custom_constructor = kwargs.get("custom_constructor", None)
|
|
488
526
|
|
|
489
527
|
# Create YAML parser with custom constructor
|
rasa/studio/auth.py
CHANGED
|
@@ -23,12 +23,10 @@ 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__(
|
|
27
|
-
self,
|
|
28
|
-
studio_config: StudioConfig,
|
|
29
|
-
verify: bool = True,
|
|
30
|
-
) -> None:
|
|
26
|
+
def __init__(self, studio_config: StudioConfig) -> None:
|
|
31
27
|
self.config = studio_config
|
|
28
|
+
verify = not studio_config.disable_verify
|
|
29
|
+
|
|
32
30
|
self.keycloak_openid = KeycloakOpenID(
|
|
33
31
|
server_url=studio_config.authentication_server_url,
|
|
34
32
|
client_id=studio_config.client_id,
|
rasa/studio/config.py
CHANGED
|
@@ -2,13 +2,14 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
from dataclasses import dataclass
|
|
5
|
-
from typing import Dict, Optional, Text
|
|
5
|
+
from typing import Any, 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,
|
|
12
13
|
RASA_STUDIO_CLI_REALM_NAME_KEY_ENV,
|
|
13
14
|
RASA_STUDIO_CLI_STUDIO_URL_ENV,
|
|
14
15
|
STUDIO_CONFIG_KEY,
|
|
@@ -19,6 +20,7 @@ STUDIO_URL_KEY = "studio_url"
|
|
|
19
20
|
CLIENT_ID_KEY = "client_id"
|
|
20
21
|
REALM_NAME_KEY = "realm_name"
|
|
21
22
|
CLIENT_SECRET_KEY = "client_secret"
|
|
23
|
+
DISABLE_VERIFY = "disable_verify"
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
@dataclass
|
|
@@ -27,13 +29,15 @@ class StudioConfig:
|
|
|
27
29
|
studio_url: Optional[Text]
|
|
28
30
|
client_id: Optional[Text]
|
|
29
31
|
realm_name: Optional[Text]
|
|
32
|
+
disable_verify: bool = False
|
|
30
33
|
|
|
31
|
-
def to_dict(self) -> Dict[Text, Optional[
|
|
34
|
+
def to_dict(self) -> Dict[Text, Optional[Any]]:
|
|
32
35
|
return {
|
|
33
36
|
AUTH_SERVER_URL_KEY: self.authentication_server_url,
|
|
34
37
|
STUDIO_URL_KEY: self.studio_url,
|
|
35
38
|
CLIENT_ID_KEY: self.client_id,
|
|
36
39
|
REALM_NAME_KEY: self.realm_name,
|
|
40
|
+
DISABLE_VERIFY: self.disable_verify,
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
@classmethod
|
|
@@ -43,6 +47,7 @@ class StudioConfig:
|
|
|
43
47
|
studio_url=data[STUDIO_URL_KEY],
|
|
44
48
|
client_id=data[CLIENT_ID_KEY],
|
|
45
49
|
realm_name=data[REALM_NAME_KEY],
|
|
50
|
+
disable_verify=data.get(DISABLE_VERIFY, False),
|
|
46
51
|
)
|
|
47
52
|
|
|
48
53
|
def write_config(self) -> None:
|
|
@@ -73,7 +78,7 @@ class StudioConfig:
|
|
|
73
78
|
config = read_global_config_value(STUDIO_CONFIG_KEY, unavailable_ok=True)
|
|
74
79
|
|
|
75
80
|
if config is None:
|
|
76
|
-
return StudioConfig(None, None, None, None)
|
|
81
|
+
return StudioConfig(None, None, None, None, False)
|
|
77
82
|
|
|
78
83
|
if not isinstance(config, dict):
|
|
79
84
|
raise ValueError(
|
|
@@ -83,7 +88,7 @@ class StudioConfig:
|
|
|
83
88
|
)
|
|
84
89
|
|
|
85
90
|
for key in config:
|
|
86
|
-
if not isinstance(config[key], str):
|
|
91
|
+
if not isinstance(config[key], str) and key != DISABLE_VERIFY:
|
|
87
92
|
raise ValueError(
|
|
88
93
|
"Invalid config file format. "
|
|
89
94
|
f"Key '{key}' is not a text value."
|
|
@@ -102,6 +107,9 @@ class StudioConfig:
|
|
|
102
107
|
studio_url=StudioConfig._read_env_value(RASA_STUDIO_CLI_STUDIO_URL_ENV),
|
|
103
108
|
client_id=StudioConfig._read_env_value(RASA_STUDIO_CLI_CLIENT_ID_KEY_ENV),
|
|
104
109
|
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
|
+
),
|
|
105
113
|
)
|
|
106
114
|
|
|
107
115
|
@staticmethod
|
|
@@ -124,4 +132,5 @@ class StudioConfig:
|
|
|
124
132
|
studio_url=self.studio_url or other.studio_url,
|
|
125
133
|
client_id=self.client_id or other.client_id,
|
|
126
134
|
realm_name=self.realm_name or other.realm_name,
|
|
135
|
+
disable_verify=self.disable_verify or other.disable_verify,
|
|
127
136
|
)
|
rasa/studio/constants.py
CHANGED
|
@@ -10,6 +10,7 @@ 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"
|
|
13
14
|
|
|
14
15
|
STUDIO_NLU_FILENAME = "studio_nlu.yml"
|
|
15
16
|
STUDIO_DOMAIN_FILENAME = "studio_domain.yml"
|
rasa/studio/data_handler.py
CHANGED
|
@@ -76,7 +76,9 @@ class StudioDataHandler:
|
|
|
76
76
|
|
|
77
77
|
return request
|
|
78
78
|
|
|
79
|
-
def _make_request(
|
|
79
|
+
def _make_request(
|
|
80
|
+
self, GQL_req: Dict[Any, Any], verify: bool = True
|
|
81
|
+
) -> Dict[Any, Any]:
|
|
80
82
|
token = KeycloakTokenReader().get_token()
|
|
81
83
|
if token.is_expired():
|
|
82
84
|
token = self.refresh_token(token)
|
|
@@ -93,6 +95,7 @@ class StudioDataHandler:
|
|
|
93
95
|
"Authorization": f"{token.token_type} {token.access_token}",
|
|
94
96
|
"Content-Type": "application/json",
|
|
95
97
|
},
|
|
98
|
+
verify=verify,
|
|
96
99
|
)
|
|
97
100
|
|
|
98
101
|
if res.status_code != 200:
|
|
@@ -128,7 +131,9 @@ class StudioDataHandler:
|
|
|
128
131
|
The data from Rasa Studio.
|
|
129
132
|
"""
|
|
130
133
|
GQL_req = self._build_request()
|
|
131
|
-
|
|
134
|
+
verify = not self.studio_config.disable_verify
|
|
135
|
+
|
|
136
|
+
response = self._make_request(GQL_req, verify=verify)
|
|
132
137
|
self._extract_data(response)
|
|
133
138
|
|
|
134
139
|
def request_data(
|
|
@@ -145,7 +150,9 @@ class StudioDataHandler:
|
|
|
145
150
|
The data from Rasa Studio.
|
|
146
151
|
"""
|
|
147
152
|
GQL_req = self._build_request(intent_names, entity_names)
|
|
148
|
-
|
|
153
|
+
verify = not self.studio_config.disable_verify
|
|
154
|
+
|
|
155
|
+
response = self._make_request(GQL_req, verify=verify)
|
|
149
156
|
self._extract_data(response)
|
|
150
157
|
|
|
151
158
|
def get_config(self) -> Optional[str]:
|
rasa/studio/upload.py
CHANGED
|
@@ -56,29 +56,37 @@ 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
|
-
|
|
59
|
+
studio_config = StudioConfig.read_config()
|
|
60
|
+
endpoint = studio_config.studio_url
|
|
61
|
+
verify = not studio_config.disable_verify
|
|
62
|
+
|
|
60
63
|
if not endpoint:
|
|
61
64
|
rasa.shared.utils.cli.print_error_and_exit(
|
|
62
65
|
"No GraphQL endpoint found in config. Please run `rasa studio config`."
|
|
63
66
|
)
|
|
64
|
-
|
|
65
|
-
structlogger.info(
|
|
66
|
-
"rasa.studio.upload.loading_data", event_info="Loading data..."
|
|
67
|
-
)
|
|
67
|
+
return
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
if not is_auth_working(endpoint, verify):
|
|
70
|
+
rasa.shared.utils.cli.print_error_and_exit(
|
|
71
|
+
"Authentication is invalid or expired. Please run `rasa studio login`."
|
|
71
72
|
)
|
|
73
|
+
return
|
|
72
74
|
|
|
73
|
-
|
|
74
|
-
args.config, "config", DEFAULT_CONFIG_PATH
|
|
75
|
-
)
|
|
75
|
+
structlogger.info("rasa.studio.upload.loading_data", event_info="Loading data...")
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
args.domain = rasa.cli.utils.get_validated_path(
|
|
78
|
+
args.domain, "domain", DEFAULT_DOMAIN_PATHS
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
args.config = rasa.cli.utils.get_validated_path(
|
|
82
|
+
args.config, "config", DEFAULT_CONFIG_PATH
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# check safely if args.calm is set and not fail if not
|
|
86
|
+
if hasattr(args, "calm") and args.calm:
|
|
87
|
+
upload_calm_assistant(args, endpoint, verify=verify)
|
|
88
|
+
else:
|
|
89
|
+
upload_nlu_assistant(args, endpoint, verify=verify)
|
|
82
90
|
|
|
83
91
|
|
|
84
92
|
config_keys = [
|
|
@@ -121,12 +129,18 @@ def _get_assistant_name(config: Dict[Text, Any]) -> str:
|
|
|
121
129
|
),
|
|
122
130
|
)
|
|
123
131
|
|
|
124
|
-
structlogger.info(
|
|
132
|
+
structlogger.info(
|
|
133
|
+
"rasa.studio.upload.name_selected",
|
|
134
|
+
event_info=f"Uploading assistant with the name '{assistant_name}'.",
|
|
135
|
+
assistant_name=assistant_name,
|
|
136
|
+
)
|
|
125
137
|
return assistant_name
|
|
126
138
|
|
|
127
139
|
|
|
128
140
|
@with_studio_error_handler
|
|
129
|
-
def upload_calm_assistant(
|
|
141
|
+
def upload_calm_assistant(
|
|
142
|
+
args: argparse.Namespace, endpoint: str, verify: bool = True
|
|
143
|
+
) -> StudioResult:
|
|
130
144
|
"""Uploads the CALM assistant data to Rasa Studio.
|
|
131
145
|
|
|
132
146
|
Args:
|
|
@@ -215,12 +229,16 @@ def upload_calm_assistant(args: argparse.Namespace, endpoint: str) -> StudioResu
|
|
|
215
229
|
nlu_yaml=nlu_examples_yaml,
|
|
216
230
|
)
|
|
217
231
|
|
|
218
|
-
structlogger.info(
|
|
219
|
-
|
|
232
|
+
structlogger.info(
|
|
233
|
+
"rasa.studio.upload.calm", event_info="Uploading to Rasa Studio..."
|
|
234
|
+
)
|
|
235
|
+
return make_request(endpoint, graphql_req, verify)
|
|
220
236
|
|
|
221
237
|
|
|
222
238
|
@with_studio_error_handler
|
|
223
|
-
def upload_nlu_assistant(
|
|
239
|
+
def upload_nlu_assistant(
|
|
240
|
+
args: argparse.Namespace, endpoint: str, verify: bool = True
|
|
241
|
+
) -> StudioResult:
|
|
224
242
|
"""Uploads the classic (dm1) assistant data to Rasa Studio.
|
|
225
243
|
|
|
226
244
|
Args:
|
|
@@ -230,10 +248,14 @@ def upload_nlu_assistant(args: argparse.Namespace, endpoint: str) -> StudioResul
|
|
|
230
248
|
- intents: The intents to upload
|
|
231
249
|
- entities: The entities to upload
|
|
232
250
|
endpoint: The studio endpoint
|
|
251
|
+
verify: Whether to verify SSL
|
|
233
252
|
Returns:
|
|
234
253
|
None
|
|
235
254
|
"""
|
|
236
|
-
structlogger.info(
|
|
255
|
+
structlogger.info(
|
|
256
|
+
"rasa.studio.upload.nlu_data_read",
|
|
257
|
+
event_info="Found DM1 assistant data, parsing...",
|
|
258
|
+
)
|
|
237
259
|
importer = TrainingDataImporter.load_from_dict(
|
|
238
260
|
domain_path=args.domain, training_data_paths=args.data, config_path=args.config
|
|
239
261
|
)
|
|
@@ -250,7 +272,9 @@ def upload_nlu_assistant(args: argparse.Namespace, endpoint: str) -> StudioResul
|
|
|
250
272
|
|
|
251
273
|
assistant_name = _get_assistant_name(config)
|
|
252
274
|
|
|
253
|
-
structlogger.info(
|
|
275
|
+
structlogger.info(
|
|
276
|
+
"rasa.studio.upload.nlu_data_validate", event_info="Validating data..."
|
|
277
|
+
)
|
|
254
278
|
_check_for_missing_primitives(
|
|
255
279
|
intents, entities, intents_from_files, entities_from_files
|
|
256
280
|
)
|
|
@@ -267,16 +291,41 @@ def upload_nlu_assistant(args: argparse.Namespace, endpoint: str) -> StudioResul
|
|
|
267
291
|
|
|
268
292
|
graphql_req = build_request(assistant_name, nlu_examples_yaml, domain_yaml)
|
|
269
293
|
|
|
270
|
-
structlogger.info(
|
|
271
|
-
|
|
294
|
+
structlogger.info(
|
|
295
|
+
"rasa.studio.upload.nlu", event_info="Uploading to Rasa Studio..."
|
|
296
|
+
)
|
|
297
|
+
return make_request(endpoint, graphql_req, verify)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def is_auth_working(endpoint: str, verify: bool = True) -> bool:
|
|
301
|
+
"""Send a test request to Studio to check if auth is working."""
|
|
302
|
+
result = make_request(
|
|
303
|
+
endpoint,
|
|
304
|
+
{
|
|
305
|
+
"operationName": "LicenseDetails",
|
|
306
|
+
"query": (
|
|
307
|
+
"query LicenseDetails {\n"
|
|
308
|
+
" licenseDetails {\n"
|
|
309
|
+
" valid\n"
|
|
310
|
+
" scopes\n"
|
|
311
|
+
" __typename\n"
|
|
312
|
+
" }\n"
|
|
313
|
+
"}"
|
|
314
|
+
),
|
|
315
|
+
"variables": {},
|
|
316
|
+
},
|
|
317
|
+
verify,
|
|
318
|
+
)
|
|
319
|
+
return result.was_successful
|
|
272
320
|
|
|
273
321
|
|
|
274
|
-
def make_request(endpoint: str, graphql_req: Dict) -> StudioResult:
|
|
322
|
+
def make_request(endpoint: str, graphql_req: Dict, verify: bool = True) -> StudioResult:
|
|
275
323
|
"""Makes a request to the studio endpoint to upload data.
|
|
276
324
|
|
|
277
325
|
Args:
|
|
278
326
|
endpoint: The studio endpoint
|
|
279
327
|
graphql_req: The graphql request
|
|
328
|
+
verify: Whether to verify SSL
|
|
280
329
|
"""
|
|
281
330
|
token = KeycloakTokenReader().get_token()
|
|
282
331
|
res = requests.post(
|
|
@@ -286,6 +335,7 @@ def make_request(endpoint: str, graphql_req: Dict) -> StudioResult:
|
|
|
286
335
|
"Authorization": f"{token.token_type} {token.access_token}",
|
|
287
336
|
"Content-Type": "application/json",
|
|
288
337
|
},
|
|
338
|
+
verify=verify,
|
|
289
339
|
)
|
|
290
340
|
|
|
291
341
|
if results_logger.response_has_errors(res.json()):
|
|
@@ -301,7 +351,12 @@ def _add_missing_entities(
|
|
|
301
351
|
for entity in entities_from_intents:
|
|
302
352
|
if entity not in entities:
|
|
303
353
|
structlogger.warning(
|
|
304
|
-
|
|
354
|
+
"rasa.studio.upload.adding_missing_entity",
|
|
355
|
+
event_info=(
|
|
356
|
+
f"Adding entity '{entity}' to upload "
|
|
357
|
+
"since it is used in an intent."
|
|
358
|
+
),
|
|
359
|
+
entity=entity,
|
|
305
360
|
)
|
|
306
361
|
all_entities.append(entity)
|
|
307
362
|
return all_entities
|