rasa-pro 3.8.18__py3-none-any.whl → 3.9.15__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 +6 -42
- rasa/__main__.py +14 -9
- rasa/anonymization/anonymization_pipeline.py +0 -1
- rasa/anonymization/anonymization_rule_executor.py +3 -3
- rasa/anonymization/utils.py +4 -3
- rasa/api.py +2 -2
- rasa/cli/arguments/default_arguments.py +1 -1
- rasa/cli/arguments/run.py +2 -2
- rasa/cli/arguments/test.py +1 -1
- rasa/cli/arguments/train.py +10 -10
- rasa/cli/e2e_test.py +27 -7
- rasa/cli/export.py +0 -1
- rasa/cli/license.py +3 -3
- rasa/cli/project_templates/calm/actions/action_template.py +1 -1
- rasa/cli/project_templates/calm/config.yml +1 -1
- rasa/cli/project_templates/calm/credentials.yml +1 -1
- rasa/cli/project_templates/calm/data/flows/add_contact.yml +1 -1
- rasa/cli/project_templates/calm/data/flows/remove_contact.yml +1 -1
- rasa/cli/project_templates/calm/domain/add_contact.yml +8 -2
- rasa/cli/project_templates/calm/domain/list_contacts.yml +3 -0
- rasa/cli/project_templates/calm/domain/remove_contact.yml +9 -2
- rasa/cli/project_templates/calm/domain/shared.yml +5 -0
- rasa/cli/project_templates/calm/endpoints.yml +4 -4
- rasa/cli/project_templates/default/actions/actions.py +1 -1
- rasa/cli/project_templates/default/config.yml +5 -5
- rasa/cli/project_templates/default/credentials.yml +1 -1
- rasa/cli/project_templates/default/endpoints.yml +4 -4
- rasa/cli/project_templates/default/tests/test_stories.yml +1 -1
- rasa/cli/project_templates/tutorial/config.yml +1 -1
- rasa/cli/project_templates/tutorial/credentials.yml +1 -1
- rasa/cli/project_templates/tutorial/data/patterns.yml +6 -0
- rasa/cli/project_templates/tutorial/domain.yml +4 -0
- rasa/cli/project_templates/tutorial/endpoints.yml +6 -6
- rasa/cli/run.py +0 -1
- rasa/cli/scaffold.py +3 -2
- rasa/cli/studio/download.py +11 -0
- rasa/cli/studio/studio.py +180 -24
- rasa/cli/studio/upload.py +0 -8
- rasa/cli/telemetry.py +18 -6
- rasa/cli/utils.py +21 -10
- rasa/cli/x.py +3 -2
- rasa/constants.py +1 -1
- rasa/core/actions/action.py +90 -315
- rasa/core/actions/action_exceptions.py +24 -0
- rasa/core/actions/constants.py +3 -0
- rasa/core/actions/custom_action_executor.py +188 -0
- rasa/core/actions/forms.py +11 -7
- rasa/core/actions/grpc_custom_action_executor.py +251 -0
- rasa/core/actions/http_custom_action_executor.py +140 -0
- rasa/core/actions/loops.py +3 -0
- rasa/core/actions/two_stage_fallback.py +1 -1
- rasa/core/agent.py +2 -4
- rasa/core/brokers/pika.py +1 -2
- rasa/core/channels/audiocodes.py +1 -1
- rasa/core/channels/botframework.py +0 -1
- rasa/core/channels/callback.py +0 -1
- rasa/core/channels/console.py +6 -8
- rasa/core/channels/development_inspector.py +1 -1
- rasa/core/channels/facebook.py +0 -3
- rasa/core/channels/hangouts.py +0 -6
- rasa/core/channels/inspector/dist/assets/{arc-5623b6dc.js → arc-b6e548fe.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-d0fbc5ce-685c106a.js → c4Diagram-d0fbc5ce-fa03ac9e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-936ed81e-8cbed007.js → classDiagram-936ed81e-ee67392a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-c3cb15f1-5889cf12.js → classDiagram-v2-c3cb15f1-9b283fae.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{createText-62fc7601-24c249d7.js → createText-62fc7601-8b6fcc2a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-f2ad444c-7dd06a75.js → edges-f2ad444c-22e77f4f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9d236eb7-62c1e54c.js → erDiagram-9d236eb7-60ffc87f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-1972c806-ce49b86f.js → flowDb-1972c806-9dd802e4.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-7ea5b25a-4067e48f.js → flowDiagram-7ea5b25a-5fa1912f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-1844e5a5.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-abe16c3d-59fe4051.js → flowchart-elk-definition-abe16c3d-622a1fd2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-9b5ea136-47e3a43b.js → ganttDiagram-9b5ea136-e285a63a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-99d0ae7c-5a2ac0d9.js → gitGraphDiagram-99d0ae7c-f237bdca.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-2c4b9a3b-dfb8efc4.js → index-2c4b9a3b-4b03d70e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-268a75c0.js → index-a5d3e69d.js} +4 -4
- rasa/core/channels/inspector/dist/assets/{infoDiagram-736b4530-b0c470f2.js → infoDiagram-736b4530-72a0fa5f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-df861f2b-2edb829a.js → journeyDiagram-df861f2b-82218c41.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-b6873d69.js → layout-78cff630.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-1efc5781.js → line-5038b469.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-661e9b94.js → linear-c4fc4098.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-beec6740-2d2e727f.js → mindmap-definition-beec6740-c33c8ea6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-dbbf0591-9d3ea93d.js → pieDiagram-dbbf0591-a8d03059.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-4d7f4fd6-06a178a2.js → quadrantDiagram-4d7f4fd6-6a0e56b2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-6fc4c22a-0bfedffc.js → requirementDiagram-6fc4c22a-2dc7c7bd.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-8f13d901-d76d0a04.js → sankeyDiagram-8f13d901-2360fe39.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-b655622a-37bb4341.js → sequenceDiagram-b655622a-41b9f9ad.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-59f0c015-f52f7f57.js → stateDiagram-59f0c015-0aad326f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-2b26beab-4a986a20.js → stateDiagram-v2-2b26beab-9847d984.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-080da4f6-7dd9ae12.js → styles-080da4f6-564d890e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-3dcbcfbf-46e1ca14.js → styles-3dcbcfbf-38957613.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9c745c82-4a97439a.js → styles-9c745c82-f0fc6921.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-4835440b-823917a3.js → svgDrawCommon-4835440b-ef3c5a77.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-5b62e21b-9ea72896.js → timeline-definition-5b62e21b-bf3e91c1.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-2b33534f-b631a8b6.js → xychartDiagram-2b33534f-4d4026c0.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +1 -1
- rasa/core/channels/inspector/src/components/DiagramFlow.tsx +10 -0
- rasa/core/channels/inspector/src/helpers/formatters.test.ts +4 -7
- rasa/core/channels/inspector/src/helpers/formatters.ts +3 -2
- rasa/core/channels/rest.py +36 -21
- rasa/core/channels/rocketchat.py +0 -1
- rasa/core/channels/socketio.py +1 -1
- rasa/core/channels/telegram.py +3 -3
- rasa/core/channels/webexteams.py +0 -1
- rasa/core/concurrent_lock_store.py +1 -1
- rasa/core/evaluation/marker_base.py +1 -3
- rasa/core/evaluation/marker_stats.py +1 -2
- rasa/core/featurizers/single_state_featurizer.py +3 -26
- rasa/core/featurizers/tracker_featurizers.py +18 -122
- rasa/core/information_retrieval/__init__.py +7 -0
- rasa/core/information_retrieval/faiss.py +9 -4
- rasa/core/information_retrieval/information_retrieval.py +64 -7
- rasa/core/information_retrieval/milvus.py +7 -14
- rasa/core/information_retrieval/qdrant.py +8 -15
- rasa/core/lock_store.py +0 -1
- rasa/core/migrate.py +1 -2
- rasa/core/nlg/callback.py +3 -4
- rasa/core/policies/enterprise_search_policy.py +86 -22
- rasa/core/policies/enterprise_search_prompt_template.jinja2 +4 -41
- rasa/core/policies/enterprise_search_prompt_with_citation_template.jinja2 +60 -0
- rasa/core/policies/flows/flow_executor.py +104 -2
- rasa/core/policies/intentless_policy.py +7 -9
- rasa/core/policies/memoization.py +3 -3
- rasa/core/policies/policy.py +18 -9
- rasa/core/policies/rule_policy.py +8 -11
- rasa/core/policies/ted_policy.py +61 -88
- rasa/core/policies/unexpected_intent_policy.py +8 -17
- rasa/core/processor.py +136 -47
- rasa/core/run.py +41 -25
- rasa/core/secrets_manager/endpoints.py +2 -2
- rasa/core/secrets_manager/vault.py +6 -8
- rasa/core/test.py +3 -5
- rasa/core/tracker_store.py +49 -14
- rasa/core/train.py +1 -3
- rasa/core/training/interactive.py +9 -6
- rasa/core/utils.py +5 -10
- rasa/dialogue_understanding/coexistence/intent_based_router.py +11 -4
- rasa/dialogue_understanding/coexistence/llm_based_router.py +2 -3
- rasa/dialogue_understanding/commands/__init__.py +4 -0
- rasa/dialogue_understanding/commands/can_not_handle_command.py +9 -0
- rasa/dialogue_understanding/commands/cancel_flow_command.py +9 -0
- rasa/dialogue_understanding/commands/change_flow_command.py +38 -0
- rasa/dialogue_understanding/commands/chit_chat_answer_command.py +9 -0
- rasa/dialogue_understanding/commands/clarify_command.py +9 -0
- rasa/dialogue_understanding/commands/correct_slots_command.py +9 -0
- rasa/dialogue_understanding/commands/error_command.py +12 -0
- rasa/dialogue_understanding/commands/handle_code_change_command.py +9 -0
- rasa/dialogue_understanding/commands/human_handoff_command.py +9 -0
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +9 -0
- rasa/dialogue_understanding/commands/noop_command.py +9 -0
- rasa/dialogue_understanding/commands/set_slot_command.py +38 -3
- rasa/dialogue_understanding/commands/skip_question_command.py +9 -0
- rasa/dialogue_understanding/commands/start_flow_command.py +9 -0
- rasa/dialogue_understanding/generator/__init__.py +16 -1
- rasa/dialogue_understanding/generator/command_generator.py +92 -6
- rasa/dialogue_understanding/generator/constants.py +18 -0
- rasa/dialogue_understanding/generator/flow_retrieval.py +7 -5
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +467 -0
- rasa/dialogue_understanding/generator/llm_command_generator.py +39 -609
- rasa/dialogue_understanding/generator/multi_step/__init__.py +0 -0
- rasa/dialogue_understanding/generator/multi_step/fill_slots_prompt.jinja2 +62 -0
- rasa/dialogue_understanding/generator/multi_step/handle_flows_prompt.jinja2 +38 -0
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +827 -0
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +69 -8
- rasa/dialogue_understanding/generator/single_step/__init__.py +0 -0
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +345 -0
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +36 -31
- rasa/dialogue_understanding/processor/command_processor.py +112 -3
- rasa/e2e_test/constants.py +1 -0
- rasa/e2e_test/e2e_test_case.py +44 -0
- rasa/e2e_test/e2e_test_runner.py +114 -11
- rasa/e2e_test/e2e_test_schema.yml +18 -0
- rasa/engine/caching.py +0 -1
- rasa/engine/graph.py +18 -6
- rasa/engine/recipes/config_files/default_config.yml +3 -3
- rasa/engine/recipes/default_components.py +1 -1
- rasa/engine/recipes/default_recipe.py +4 -5
- rasa/engine/recipes/recipe.py +1 -1
- rasa/engine/runner/dask.py +3 -9
- rasa/engine/storage/local_model_storage.py +0 -2
- rasa/engine/validation.py +179 -145
- rasa/exceptions.py +2 -2
- rasa/graph_components/validators/default_recipe_validator.py +3 -5
- rasa/hooks.py +0 -1
- rasa/model.py +1 -1
- rasa/model_training.py +1 -0
- rasa/nlu/classifiers/diet_classifier.py +33 -52
- 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 +54 -97
- rasa/nlu/extractors/duckling_entity_extractor.py +1 -1
- rasa/nlu/featurizers/dense_featurizer/convert_featurizer.py +1 -5
- rasa/nlu/featurizers/dense_featurizer/lm_featurizer.py +0 -4
- rasa/nlu/featurizers/featurizer.py +1 -1
- rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +18 -49
- rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +26 -64
- rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +3 -5
- rasa/nlu/persistor.py +68 -26
- rasa/nlu/selectors/response_selector.py +7 -10
- rasa/nlu/test.py +0 -3
- rasa/nlu/utils/hugging_face/registry.py +1 -1
- rasa/nlu/utils/spacy_utils.py +1 -3
- rasa/server.py +22 -7
- rasa/shared/constants.py +12 -1
- rasa/shared/core/command_payload_reader.py +109 -0
- rasa/shared/core/constants.py +4 -5
- rasa/shared/core/domain.py +57 -56
- rasa/shared/core/events.py +4 -7
- rasa/shared/core/flows/flow.py +9 -0
- rasa/shared/core/flows/flows_list.py +12 -0
- rasa/shared/core/flows/steps/action.py +7 -2
- rasa/shared/core/generator.py +12 -11
- rasa/shared/core/slot_mappings.py +315 -24
- rasa/shared/core/slots.py +4 -2
- rasa/shared/core/trackers.py +32 -14
- rasa/shared/core/training_data/loading.py +0 -1
- rasa/shared/core/training_data/story_reader/story_reader.py +3 -3
- rasa/shared/core/training_data/story_reader/yaml_story_reader.py +11 -11
- rasa/shared/core/training_data/story_writer/yaml_story_writer.py +5 -3
- rasa/shared/core/training_data/structures.py +1 -1
- rasa/shared/core/training_data/visualization.py +1 -1
- rasa/shared/data.py +58 -1
- rasa/shared/exceptions.py +36 -2
- rasa/shared/importers/importer.py +1 -2
- rasa/shared/importers/rasa.py +0 -1
- rasa/shared/nlu/constants.py +2 -0
- rasa/shared/nlu/training_data/entities_parser.py +1 -2
- rasa/shared/nlu/training_data/features.py +2 -120
- rasa/shared/nlu/training_data/formats/dialogflow.py +3 -2
- rasa/shared/nlu/training_data/formats/rasa_yaml.py +3 -5
- rasa/shared/nlu/training_data/formats/readerwriter.py +0 -1
- rasa/shared/nlu/training_data/message.py +13 -0
- rasa/shared/nlu/training_data/training_data.py +0 -2
- rasa/shared/providers/openai/session_handler.py +2 -2
- rasa/shared/utils/constants.py +3 -0
- rasa/shared/utils/io.py +11 -1
- rasa/shared/utils/llm.py +1 -2
- rasa/shared/utils/pykwalify_extensions.py +1 -0
- rasa/shared/utils/schemas/domain.yml +3 -0
- rasa/shared/utils/yaml.py +44 -35
- rasa/studio/auth.py +26 -10
- rasa/studio/constants.py +2 -0
- rasa/studio/data_handler.py +114 -107
- rasa/studio/download.py +160 -27
- rasa/studio/results_logger.py +137 -0
- rasa/studio/train.py +6 -7
- rasa/studio/upload.py +159 -134
- rasa/telemetry.py +188 -34
- rasa/tracing/config.py +18 -3
- rasa/tracing/constants.py +26 -2
- rasa/tracing/instrumentation/attribute_extractors.py +50 -41
- rasa/tracing/instrumentation/instrumentation.py +290 -44
- rasa/tracing/instrumentation/intentless_policy_instrumentation.py +7 -5
- rasa/tracing/instrumentation/metrics.py +109 -21
- rasa/tracing/metric_instrument_provider.py +83 -3
- rasa/utils/cli.py +2 -1
- rasa/utils/common.py +1 -1
- rasa/utils/endpoints.py +1 -2
- rasa/utils/io.py +72 -6
- rasa/utils/licensing.py +246 -31
- rasa/utils/ml_utils.py +1 -1
- rasa/utils/tensorflow/data_generator.py +1 -1
- rasa/utils/tensorflow/environment.py +1 -1
- rasa/utils/tensorflow/model_data.py +201 -12
- rasa/utils/tensorflow/model_data_utils.py +499 -500
- rasa/utils/tensorflow/models.py +5 -6
- rasa/utils/tensorflow/rasa_layers.py +15 -15
- rasa/utils/train_utils.py +1 -1
- rasa/utils/url_tools.py +53 -0
- rasa/validator.py +305 -3
- rasa/version.py +1 -1
- {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.15.dist-info}/METADATA +25 -61
- {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.15.dist-info}/RECORD +276 -259
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-85583a23.js +0 -1
- rasa/utils/tensorflow/feature_array.py +0 -370
- /rasa/dialogue_understanding/generator/{command_prompt_template.jinja2 → single_step/command_prompt_template.jinja2} +0 -0
- {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.15.dist-info}/NOTICE +0 -0
- {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.15.dist-info}/WHEEL +0 -0
- {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.15.dist-info}/entry_points.txt +0 -0
rasa/shared/utils/yaml.py
CHANGED
|
@@ -1,40 +1,25 @@
|
|
|
1
|
-
from io import StringIO
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
import re
|
|
4
|
-
from collections import OrderedDict
|
|
5
1
|
import logging
|
|
6
2
|
import os
|
|
3
|
+
import re
|
|
4
|
+
from collections import OrderedDict
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from dataclasses import field
|
|
7
|
+
from functools import lru_cache
|
|
8
|
+
from io import StringIO
|
|
9
|
+
from pathlib import Path
|
|
7
10
|
from typing import Dict, List, Optional, Any, Callable, Tuple, Union
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
import jsonschema
|
|
10
13
|
from importlib_resources import files
|
|
11
14
|
from packaging import version
|
|
12
15
|
from packaging.version import LegacyVersion
|
|
13
16
|
from pykwalify.core import Core
|
|
14
17
|
from pykwalify.errors import SchemaError
|
|
15
|
-
from
|
|
16
|
-
import jsonschema
|
|
17
|
-
from dataclasses import field
|
|
18
|
+
from ruamel import yaml as yaml
|
|
18
19
|
from ruamel.yaml import RoundTripRepresenter, YAMLError
|
|
19
20
|
from ruamel.yaml.constructor import DuplicateKeyError, BaseConstructor, ScalarNode
|
|
20
|
-
from ruamel import yaml as yaml
|
|
21
21
|
from ruamel.yaml.comments import CommentedSeq, CommentedMap
|
|
22
22
|
|
|
23
|
-
from rasa.shared.utils.constants import DEFAULT_ENCODING
|
|
24
|
-
from rasa.shared.utils.io import (
|
|
25
|
-
read_file,
|
|
26
|
-
convert_to_ordered_dict,
|
|
27
|
-
raise_warning,
|
|
28
|
-
read_json_file,
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
from rasa.shared.exceptions import (
|
|
32
|
-
YamlException,
|
|
33
|
-
YamlSyntaxException,
|
|
34
|
-
SchemaValidationError,
|
|
35
|
-
RasaException,
|
|
36
|
-
FileNotFoundException,
|
|
37
|
-
)
|
|
38
23
|
from rasa.shared.constants import (
|
|
39
24
|
MODEL_CONFIG_SCHEMA_FILE,
|
|
40
25
|
CONFIG_SCHEMA_FILE,
|
|
@@ -44,11 +29,32 @@ from rasa.shared.constants import (
|
|
|
44
29
|
SCHEMA_EXTENSIONS_FILE,
|
|
45
30
|
RESPONSES_SCHEMA_FILE,
|
|
46
31
|
)
|
|
32
|
+
from rasa.shared.exceptions import (
|
|
33
|
+
YamlException,
|
|
34
|
+
YamlSyntaxException,
|
|
35
|
+
SchemaValidationError,
|
|
36
|
+
RasaException,
|
|
37
|
+
FileNotFoundException,
|
|
38
|
+
)
|
|
39
|
+
from rasa.shared.utils.constants import (
|
|
40
|
+
DEFAULT_ENCODING,
|
|
41
|
+
READ_YAML_FILE_CACHE_MAXSIZE_ENV_VAR,
|
|
42
|
+
DEFAULT_READ_YAML_FILE_CACHE_MAXSIZE,
|
|
43
|
+
)
|
|
44
|
+
from rasa.shared.utils.io import (
|
|
45
|
+
read_file,
|
|
46
|
+
convert_to_ordered_dict,
|
|
47
|
+
raise_warning,
|
|
48
|
+
read_json_file,
|
|
49
|
+
)
|
|
47
50
|
|
|
48
51
|
logger = logging.getLogger(__name__)
|
|
49
52
|
|
|
50
53
|
KEY_TRAINING_DATA_FORMAT_VERSION = "version"
|
|
51
54
|
YAML_VERSION = (1, 2)
|
|
55
|
+
READ_YAML_FILE_CACHE_MAXSIZE = os.environ.get(
|
|
56
|
+
READ_YAML_FILE_CACHE_MAXSIZE_ENV_VAR, DEFAULT_READ_YAML_FILE_CACHE_MAXSIZE
|
|
57
|
+
)
|
|
52
58
|
|
|
53
59
|
|
|
54
60
|
@dataclass
|
|
@@ -437,8 +443,9 @@ def _is_ascii(text: str) -> bool:
|
|
|
437
443
|
return all(ord(character) < 128 for character in text)
|
|
438
444
|
|
|
439
445
|
|
|
446
|
+
@lru_cache(maxsize=READ_YAML_FILE_CACHE_MAXSIZE)
|
|
440
447
|
def read_yaml_file(
|
|
441
|
-
filename: Union[str, Path], reader_type: Union[str,
|
|
448
|
+
filename: Union[str, Path], reader_type: Union[str, Tuple[str]] = "safe"
|
|
442
449
|
) -> Union[List[Any], Dict[str, Any]]:
|
|
443
450
|
"""Parses a yaml file.
|
|
444
451
|
|
|
@@ -452,7 +459,10 @@ def read_yaml_file(
|
|
|
452
459
|
Parsed content of the file.
|
|
453
460
|
"""
|
|
454
461
|
try:
|
|
455
|
-
|
|
462
|
+
fixed_reader_type = (
|
|
463
|
+
list(reader_type) if isinstance(reader_type, tuple) else reader_type
|
|
464
|
+
)
|
|
465
|
+
return read_yaml(read_file(filename, DEFAULT_ENCODING), fixed_reader_type)
|
|
456
466
|
except (YAMLError, DuplicateKeyError) as e:
|
|
457
467
|
raise YamlSyntaxException(filename, e)
|
|
458
468
|
|
|
@@ -635,14 +645,14 @@ def validate_training_data_format_version(
|
|
|
635
645
|
"""Validates version on the training data content using `version` field.
|
|
636
646
|
|
|
637
647
|
Warns users if the file is not compatible with the current version of
|
|
638
|
-
Rasa
|
|
648
|
+
Rasa Pro.
|
|
639
649
|
|
|
640
650
|
Args:
|
|
641
651
|
yaml_file_content: Raw content of training data file as a dictionary.
|
|
642
652
|
filename: Name of the validated file.
|
|
643
653
|
|
|
644
654
|
Returns:
|
|
645
|
-
`True` if the file can be processed by current version of Rasa
|
|
655
|
+
`True` if the file can be processed by current version of Rasa Pro,
|
|
646
656
|
`False` otherwise.
|
|
647
657
|
"""
|
|
648
658
|
if filename:
|
|
@@ -662,7 +672,7 @@ def validate_training_data_format_version(
|
|
|
662
672
|
logger.info(
|
|
663
673
|
f"The '{KEY_TRAINING_DATA_FORMAT_VERSION}' key is missing in "
|
|
664
674
|
f"the training data file {filename}. "
|
|
665
|
-
f"Rasa
|
|
675
|
+
f"Rasa Pro will read the file as a "
|
|
666
676
|
f"version '{LATEST_TRAINING_DATA_FORMAT_VERSION}' file. "
|
|
667
677
|
f"See {DOCS_URL_TRAINING_DATA}."
|
|
668
678
|
)
|
|
@@ -680,9 +690,9 @@ def validate_training_data_format_version(
|
|
|
680
690
|
if parsed_version < latest_version:
|
|
681
691
|
raise_warning(
|
|
682
692
|
f"Training data file {filename} has a lower "
|
|
683
|
-
f"format version than your Rasa
|
|
693
|
+
f"format version than your Rasa Pro installation: "
|
|
684
694
|
f"{version_value} < {LATEST_TRAINING_DATA_FORMAT_VERSION}. "
|
|
685
|
-
f"Rasa
|
|
695
|
+
f"Rasa Pro will read the file as a version "
|
|
686
696
|
f"{LATEST_TRAINING_DATA_FORMAT_VERSION} file. "
|
|
687
697
|
f"Please update your version key to "
|
|
688
698
|
f"{LATEST_TRAINING_DATA_FORMAT_VERSION}. "
|
|
@@ -690,7 +700,6 @@ def validate_training_data_format_version(
|
|
|
690
700
|
)
|
|
691
701
|
|
|
692
702
|
if latest_version >= parsed_version:
|
|
693
|
-
|
|
694
703
|
return True
|
|
695
704
|
|
|
696
705
|
except TypeError:
|
|
@@ -699,7 +708,7 @@ def validate_training_data_format_version(
|
|
|
699
708
|
f"'{KEY_TRAINING_DATA_FORMAT_VERSION}' as string, for example:\n"
|
|
700
709
|
f"{KEY_TRAINING_DATA_FORMAT_VERSION}: "
|
|
701
710
|
f"'{LATEST_TRAINING_DATA_FORMAT_VERSION}'\n"
|
|
702
|
-
f"Rasa
|
|
711
|
+
f"Rasa Pro will read the file as a "
|
|
703
712
|
f"version '{LATEST_TRAINING_DATA_FORMAT_VERSION}' file.",
|
|
704
713
|
docs=DOCS_URL_TRAINING_DATA,
|
|
705
714
|
)
|
|
@@ -707,9 +716,9 @@ def validate_training_data_format_version(
|
|
|
707
716
|
|
|
708
717
|
raise_warning(
|
|
709
718
|
f"Training data file {filename} has a greater "
|
|
710
|
-
f"format version than your Rasa
|
|
719
|
+
f"format version than your Rasa Pro installation: "
|
|
711
720
|
f"{version_value} > {LATEST_TRAINING_DATA_FORMAT_VERSION}. "
|
|
712
|
-
f"Please consider updating to the latest version of Rasa
|
|
721
|
+
f"Please consider updating to the latest version of Rasa Pro."
|
|
713
722
|
f"This file will be skipped.",
|
|
714
723
|
docs=DOCS_URL_TRAINING_DATA,
|
|
715
724
|
)
|
rasa/studio/auth.py
CHANGED
|
@@ -6,7 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
from typing import Any, Dict, List, Optional, Text, Union
|
|
7
7
|
|
|
8
8
|
import jwt
|
|
9
|
-
from keycloak import KeycloakOpenID
|
|
9
|
+
from keycloak import KeycloakOpenID, KeycloakError
|
|
10
10
|
from rasa.shared.exceptions import RasaException
|
|
11
11
|
from rasa.shared.utils.yaml import read_yaml_file, write_yaml
|
|
12
12
|
|
|
@@ -17,6 +17,7 @@ from rasa.studio.constants import (
|
|
|
17
17
|
KEYCLOAK_REFRESH_EXPIRES_IN_KEY,
|
|
18
18
|
KEYCLOAK_REFRESH_TOKEN,
|
|
19
19
|
)
|
|
20
|
+
from rasa.studio.results_logger import with_studio_error_handler, StudioResult
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class StudioAuth:
|
|
@@ -31,26 +32,39 @@ class StudioAuth:
|
|
|
31
32
|
realm_name=studio_config.realm_name,
|
|
32
33
|
)
|
|
33
34
|
|
|
34
|
-
def
|
|
35
|
+
def health_check(self) -> bool:
|
|
36
|
+
"""Check if the Keycloak server is reachable.
|
|
35
37
|
|
|
38
|
+
Returns:
|
|
39
|
+
True if the server is reachable, False otherwise.
|
|
40
|
+
"""
|
|
36
41
|
try:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
self.keycloak_openid.well_known()
|
|
43
|
+
return True
|
|
44
|
+
except Exception:
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
@with_studio_error_handler
|
|
48
|
+
def login(
|
|
49
|
+
self, username: Text, password: Text, totp: Optional[int] = None
|
|
50
|
+
) -> StudioResult:
|
|
51
|
+
token_dict = self.keycloak_openid.token(
|
|
52
|
+
username=username, password=password, totp=totp
|
|
53
|
+
)
|
|
43
54
|
keycloak_token = self._resolve_token(token_dict)
|
|
44
55
|
|
|
45
56
|
KeycloakTokenWriter.write_token_to_file(
|
|
46
57
|
keycloak_token, token_file_location=DEFAULT_TOKEN_FILE_PATH
|
|
47
58
|
)
|
|
48
59
|
|
|
49
|
-
|
|
60
|
+
return StudioResult.success("Login successful.")
|
|
61
|
+
|
|
62
|
+
@with_studio_error_handler
|
|
63
|
+
def refresh_token(self, refresh_token: Text) -> StudioResult:
|
|
50
64
|
try:
|
|
51
65
|
token_dict = self.keycloak_openid.refresh_token(refresh_token)
|
|
52
66
|
except Exception as e:
|
|
53
|
-
raise
|
|
67
|
+
raise KeycloakError(f"Could not refresh token. Error: {e}")
|
|
54
68
|
|
|
55
69
|
keycloak_token = self._resolve_token(token_dict)
|
|
56
70
|
|
|
@@ -58,6 +72,8 @@ class StudioAuth:
|
|
|
58
72
|
keycloak_token, token_file_location=DEFAULT_TOKEN_FILE_PATH
|
|
59
73
|
)
|
|
60
74
|
|
|
75
|
+
return StudioResult.success("Token refreshed successfully.")
|
|
76
|
+
|
|
61
77
|
@staticmethod
|
|
62
78
|
def _resolve_token(token_dict: Dict[Text, Any]) -> KeycloakToken:
|
|
63
79
|
return KeycloakToken(
|
rasa/studio/constants.py
CHANGED
|
@@ -14,3 +14,5 @@ RASA_STUDIO_CLI_CLIENT_ID_KEY_ENV = "RASA_STUDIO_CLI_CLIENT_ID_KEY"
|
|
|
14
14
|
STUDIO_NLU_FILENAME = "studio_nlu.yml"
|
|
15
15
|
STUDIO_DOMAIN_FILENAME = "studio_domain.yml"
|
|
16
16
|
STUDIO_FLOWS_FILENAME = "studio_flows.yml"
|
|
17
|
+
STUDIO_CONFIG_FILENAME = "studio_config.yml"
|
|
18
|
+
STUDIO_ENDPOINTS_FILENAME = "studio_endpoints.yml"
|
rasa/studio/data_handler.py
CHANGED
|
@@ -50,7 +50,7 @@ class StudioDataHandler:
|
|
|
50
50
|
"query ExportAsEncodedYaml($input: ExportAsEncodedYamlInput!) "
|
|
51
51
|
"{ exportAsEncodedYaml(input: $input) "
|
|
52
52
|
"{ ... on ExportModernAsEncodedYamlOutput "
|
|
53
|
-
"{ nlu flows domain } "
|
|
53
|
+
"{ nlu flows domain endpoints config } "
|
|
54
54
|
"... on ExportClassicAsEncodedYamlOutput "
|
|
55
55
|
"{ nlu domain }}}"
|
|
56
56
|
),
|
|
@@ -148,6 +148,12 @@ class StudioDataHandler:
|
|
|
148
148
|
response = self._make_request(GQL_req)
|
|
149
149
|
self._extract_data(response)
|
|
150
150
|
|
|
151
|
+
def get_config(self) -> Optional[str]:
|
|
152
|
+
return self.config
|
|
153
|
+
|
|
154
|
+
def get_endpoints(self) -> Optional[str]:
|
|
155
|
+
return self.endpoints
|
|
156
|
+
|
|
151
157
|
def _validate_response(self, response: dict) -> bool:
|
|
152
158
|
"""Validates the response from Rasa Studio.
|
|
153
159
|
|
|
@@ -184,6 +190,8 @@ class StudioDataHandler:
|
|
|
184
190
|
self.nlu = self._decode_response(return_data.get("nlu"))
|
|
185
191
|
self.domain = self._decode_response(return_data.get("domain"))
|
|
186
192
|
self.flows = self._decode_response(return_data.get("flows"))
|
|
193
|
+
self.config = self._decode_response(return_data.get("config"))
|
|
194
|
+
self.endpoints = self._decode_response(return_data.get("endpoints"))
|
|
187
195
|
|
|
188
196
|
if not self.has_nlu() and not self.has_flows():
|
|
189
197
|
raise RasaException("No nlu or flows data in Studio response.")
|
|
@@ -195,114 +203,113 @@ class StudioDataHandler:
|
|
|
195
203
|
return base64.b64decode(data).decode("utf-8")
|
|
196
204
|
|
|
197
205
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
def create_new_domain_from_diff(self) -> Dict:
|
|
218
|
-
"""Create a new domain file from the diff."""
|
|
219
|
-
if self.studio_domain is None or self.original_domain is None:
|
|
220
|
-
return {}
|
|
221
|
-
return self._domain_from_diff_rec(self.studio_domain, self.original_domain)
|
|
222
|
-
|
|
223
|
-
@staticmethod
|
|
224
|
-
def _domain_from_diff_rec(studio: Dict, original: Dict) -> Dict:
|
|
225
|
-
ret_dict = {}
|
|
226
|
-
for key in studio:
|
|
227
|
-
if key not in original:
|
|
228
|
-
ret_dict[key] = studio[key]
|
|
229
|
-
elif isinstance(studio[key], dict):
|
|
230
|
-
ret_dict[key] = DataDiffGenerator._domain_from_diff_rec(
|
|
231
|
-
studio[key], original[key]
|
|
232
|
-
)
|
|
233
|
-
# remove empty diffs
|
|
234
|
-
if not ret_dict[key]:
|
|
235
|
-
del ret_dict[key]
|
|
236
|
-
elif key not in [KEY_SLOTS, KEY_RESPONSES]:
|
|
237
|
-
# copy over the whole key not just the diff in case of items
|
|
238
|
-
# to get complete (valid) data not just the diff
|
|
239
|
-
ret_dict[key] = studio[key]
|
|
240
|
-
elif isinstance(studio[key], list):
|
|
241
|
-
ret_dict[key] = []
|
|
242
|
-
for item in studio[key]:
|
|
243
|
-
if item not in original[key]:
|
|
244
|
-
ret_dict[key].append(item)
|
|
245
|
-
|
|
246
|
-
# if list is empty, remove it
|
|
247
|
-
if not ret_dict[key]:
|
|
248
|
-
del ret_dict[key]
|
|
249
|
-
|
|
250
|
-
return ret_dict
|
|
251
|
-
|
|
252
|
-
def create_new_nlu_from_diff(self) -> Dict:
|
|
253
|
-
"""Create a new nlu file from the diff."""
|
|
254
|
-
if self.studio_nlu is None or self.original_nlu is None:
|
|
255
|
-
return {"nlu": []}
|
|
256
|
-
|
|
257
|
-
nlu_diff = []
|
|
258
|
-
nlu_new_examples = [
|
|
259
|
-
new for new in self.studio_nlu["nlu"] if new not in self.original_nlu["nlu"]
|
|
260
|
-
]
|
|
261
|
-
if nlu_new_examples:
|
|
262
|
-
for new_example in nlu_new_examples:
|
|
263
|
-
nlu_diff.append(new_example)
|
|
264
|
-
intent = new_example.get("intent")
|
|
265
|
-
if intent:
|
|
266
|
-
self._diff_nlu_examples(new_example, nlu_diff, "intent", intent)
|
|
267
|
-
synonym = new_example.get("synonym")
|
|
268
|
-
if synonym:
|
|
269
|
-
self._diff_nlu_examples(new_example, nlu_diff, "synonym", synonym)
|
|
270
|
-
|
|
271
|
-
return {"nlu": nlu_diff}
|
|
272
|
-
|
|
273
|
-
def create_new_flows_from_diff(self) -> List[Flow]:
|
|
274
|
-
"""Create a new flows file from the diff."""
|
|
275
|
-
if self.studio_flows is None or self.original_flows is None:
|
|
276
|
-
return []
|
|
277
|
-
|
|
278
|
-
flows_new = [new for new in self.studio_flows if new not in self.original_flows]
|
|
279
|
-
return flows_new
|
|
280
|
-
|
|
281
|
-
def _diff_nlu_examples(
|
|
282
|
-
self, new_example: Dict, nlu_diff: List, match_key: str, match_value: str
|
|
283
|
-
) -> None:
|
|
284
|
-
"""Creates diff of nlu data examples.
|
|
285
|
-
|
|
286
|
-
Args:
|
|
287
|
-
new_example (Dict): intent or synonym with examples
|
|
288
|
-
nlu_diff (List): list of diff examples
|
|
289
|
-
match_key (str): intent or synonym name
|
|
290
|
-
match_value (str): intent or synonym value
|
|
291
|
-
"""
|
|
292
|
-
orig = list(
|
|
293
|
-
filter(
|
|
294
|
-
lambda x: x.get(match_key) == match_value,
|
|
295
|
-
self.original_nlu["nlu"], # type: ignore[index]
|
|
206
|
+
def combine_domains(
|
|
207
|
+
studio_domain: Dict[str, Any], original_domain: Dict[str, Any]
|
|
208
|
+
) -> Dict:
|
|
209
|
+
"""Create a new domain file from the diff."""
|
|
210
|
+
if studio_domain is None or original_domain is None:
|
|
211
|
+
return {}
|
|
212
|
+
return _combine_domain_keys(studio_domain, original_domain)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _combine_domain_keys(
|
|
216
|
+
first_domain: Dict[str, Any], second_domain: Dict[str, Any]
|
|
217
|
+
) -> Dict[str, Any]:
|
|
218
|
+
combined_keys = {}
|
|
219
|
+
for key in first_domain:
|
|
220
|
+
if key not in second_domain:
|
|
221
|
+
combined_keys[key] = first_domain[key]
|
|
222
|
+
elif isinstance(first_domain[key], dict):
|
|
223
|
+
combined_keys[key] = _combine_domain_keys(
|
|
224
|
+
first_domain[key], second_domain[key]
|
|
296
225
|
)
|
|
226
|
+
# remove empty diffs
|
|
227
|
+
if not combined_keys[key]:
|
|
228
|
+
del combined_keys[key]
|
|
229
|
+
elif key not in [KEY_SLOTS, KEY_RESPONSES]:
|
|
230
|
+
# for all keys except slots and responses, we want to keep the
|
|
231
|
+
# keys from the first domain
|
|
232
|
+
combined_keys[key] = first_domain[key]
|
|
233
|
+
elif isinstance(first_domain[key], list):
|
|
234
|
+
combined_keys[key] = []
|
|
235
|
+
for item in first_domain[key]:
|
|
236
|
+
if item not in second_domain[key]:
|
|
237
|
+
combined_keys[key].append(item)
|
|
238
|
+
|
|
239
|
+
# if list is empty, remove it
|
|
240
|
+
if not combined_keys[key]:
|
|
241
|
+
del combined_keys[key]
|
|
242
|
+
|
|
243
|
+
return combined_keys
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _diff_nlu_examples(
|
|
247
|
+
new_example: Dict,
|
|
248
|
+
nlu_diff: List,
|
|
249
|
+
match_key: str,
|
|
250
|
+
match_value: str,
|
|
251
|
+
original_nlu_examples: List,
|
|
252
|
+
) -> None:
|
|
253
|
+
"""Creates diff of nlu data examples.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
new_example (Dict): intent or synonym with examples
|
|
257
|
+
nlu_diff (List): list of diff examples
|
|
258
|
+
match_key (str): intent or synonym name
|
|
259
|
+
match_value (str): intent or synonym value
|
|
260
|
+
original_nlu_examples (List): original nlu examples
|
|
261
|
+
"""
|
|
262
|
+
orig = list(
|
|
263
|
+
filter(
|
|
264
|
+
lambda x: x.get(match_key) == match_value,
|
|
265
|
+
original_nlu_examples,
|
|
297
266
|
)
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
267
|
+
)
|
|
268
|
+
if len(orig) == 1:
|
|
269
|
+
orig_ex = orig[0]["examples"].split("\n")
|
|
270
|
+
new_ex = new_example["examples"].split("\n")
|
|
271
|
+
new_example["examples"] = "\n".join(list(set(new_ex).difference(set(orig_ex))))
|
|
272
|
+
if not new_example["examples"]:
|
|
273
|
+
nlu_diff.remove(new_example)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def create_new_nlu_from_diff(
|
|
277
|
+
studio_nlu: Dict[str, Any], original_nlu: Dict[str, Any]
|
|
278
|
+
) -> Dict:
|
|
279
|
+
"""Create a new nlu file from the diff."""
|
|
280
|
+
# `or []` handles the case where the data contains the property as an empty
|
|
281
|
+
# key, example yaml:
|
|
282
|
+
# ```
|
|
283
|
+
# nlu:
|
|
284
|
+
# ```
|
|
285
|
+
# in this case, the yaml parser will return an empty dict (because it
|
|
286
|
+
# can't know that it is supposed to be a list, so we need to convert it
|
|
287
|
+
# to a list
|
|
288
|
+
studio_nlu_data = studio_nlu.get("nlu", []) or []
|
|
289
|
+
original_nlu_data = original_nlu.get("nlu", []) or []
|
|
290
|
+
|
|
291
|
+
nlu_diff = []
|
|
292
|
+
for new in studio_nlu_data:
|
|
293
|
+
if new in original_nlu_data:
|
|
294
|
+
continue
|
|
295
|
+
|
|
296
|
+
nlu_diff.append(new)
|
|
297
|
+
intent = new.get("intent")
|
|
298
|
+
if intent:
|
|
299
|
+
_diff_nlu_examples(new, nlu_diff, "intent", intent, original_nlu_data)
|
|
300
|
+
synonym = new.get("synonym")
|
|
301
|
+
if synonym:
|
|
302
|
+
_diff_nlu_examples(new, nlu_diff, "synonym", synonym, original_nlu_data)
|
|
303
|
+
|
|
304
|
+
return {"nlu": nlu_diff}
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def create_new_flows_from_diff(
|
|
308
|
+
studio_flows: List[Flow], original_flows: List[Flow]
|
|
309
|
+
) -> List[Flow]:
|
|
310
|
+
"""Create a new flows file from the diff."""
|
|
311
|
+
flows_new = [new for new in studio_flows if new not in original_flows]
|
|
312
|
+
return flows_new
|
|
306
313
|
|
|
307
314
|
|
|
308
315
|
def import_data_from_studio(
|