rasa-pro 3.11.0a4.dev2__py3-none-any.whl → 3.11.0rc1__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 +4 -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/utils.py +5 -0
- rasa/cli/x.py +8 -8
- rasa/constants.py +1 -1
- rasa/core/channels/channel.py +3 -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/jambonz.py +2 -2
- 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 +177 -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 +16 -22
- rasa/core/persistor.py +86 -39
- rasa/core/policies/enterprise_search_policy.py +159 -60
- rasa/core/policies/flows/flow_executor.py +7 -4
- rasa/core/policies/intentless_policy.py +120 -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 +58 -16
- 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 +4 -0
- rasa/dialogue_understanding/generator/flow_retrieval.py +65 -3
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +68 -26
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +57 -8
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +64 -7
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +39 -0
- rasa/dialogue_understanding/patterns/user_silence.py +37 -0
- rasa/e2e_test/e2e_test_runner.py +4 -2
- rasa/e2e_test/utils/io.py +1 -1
- rasa/engine/validation.py +297 -7
- rasa/model_manager/config.py +17 -3
- rasa/model_manager/model_api.py +16 -8
- 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 +7 -5
- 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/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_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 +12 -15
- 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 +31 -30
- 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.py +533 -0
- rasa/shared/utils/io.py +28 -6
- rasa/shared/utils/llm.py +350 -46
- rasa/shared/utils/yaml.py +11 -13
- rasa/studio/upload.py +64 -20
- rasa/telemetry.py +80 -17
- rasa/tracing/instrumentation/attribute_extractors.py +74 -17
- rasa/utils/io.py +0 -66
- rasa/utils/log_utils.py +9 -2
- 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.dev2.dist-info → rasa_pro-3.11.0rc1.dist-info}/METADATA +10 -10
- {rasa_pro-3.11.0a4.dev2.dist-info → rasa_pro-3.11.0rc1.dist-info}/RECORD +162 -146
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-587d82d8.js +0 -1
- {rasa_pro-3.11.0a4.dev2.dist-info → rasa_pro-3.11.0rc1.dist-info}/NOTICE +0 -0
- {rasa_pro-3.11.0a4.dev2.dist-info → rasa_pro-3.11.0rc1.dist-info}/WHEEL +0 -0
- {rasa_pro-3.11.0a4.dev2.dist-info → rasa_pro-3.11.0rc1.dist-info}/entry_points.txt +0 -0
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
2
3
|
import logging
|
|
3
4
|
import re
|
|
5
|
+
from typing import Any, Dict, List, Optional, Text, Tuple, Set, Type, Union
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
4
8
|
import scipy.sparse
|
|
5
|
-
from
|
|
6
|
-
from
|
|
9
|
+
from sklearn.exceptions import NotFittedError
|
|
10
|
+
from sklearn.feature_extraction.text import CountVectorizer
|
|
7
11
|
|
|
8
12
|
import rasa.shared.utils.io
|
|
9
13
|
from rasa.engine.graph import GraphComponent, ExecutionContext
|
|
10
14
|
from rasa.engine.recipes.default_recipe import DefaultV1Recipe
|
|
11
15
|
from rasa.engine.storage.resource import Resource
|
|
12
16
|
from rasa.engine.storage.storage import ModelStorage
|
|
13
|
-
from rasa.nlu.featurizers.sparse_featurizer.sparse_featurizer import SparseFeaturizer
|
|
14
|
-
from rasa.nlu.utils.spacy_utils import SpacyModel
|
|
15
|
-
from rasa.shared.constants import DOCS_URL_COMPONENTS
|
|
16
|
-
import rasa.utils.io as io_utils
|
|
17
|
-
from sklearn.exceptions import NotFittedError
|
|
18
|
-
from sklearn.feature_extraction.text import CountVectorizer
|
|
19
|
-
from rasa.shared.nlu.training_data.training_data import TrainingData
|
|
20
|
-
from rasa.shared.nlu.training_data.message import Message
|
|
21
|
-
from rasa.shared.exceptions import RasaException, FileIOException
|
|
22
17
|
from rasa.nlu.constants import (
|
|
23
18
|
TOKENS_NAMES,
|
|
24
19
|
MESSAGE_ATTRIBUTES,
|
|
25
20
|
DENSE_FEATURIZABLE_ATTRIBUTES,
|
|
26
21
|
)
|
|
22
|
+
from rasa.nlu.featurizers.sparse_featurizer.sparse_featurizer import SparseFeaturizer
|
|
23
|
+
from rasa.nlu.tokenizers.tokenizer import Tokenizer
|
|
24
|
+
from rasa.nlu.utils.spacy_utils import SpacyModel
|
|
25
|
+
from rasa.shared.constants import DOCS_URL_COMPONENTS
|
|
26
|
+
from rasa.shared.exceptions import RasaException, FileIOException
|
|
27
27
|
from rasa.shared.nlu.constants import TEXT, INTENT, INTENT_RESPONSE_KEY, ACTION_NAME
|
|
28
|
+
from rasa.shared.nlu.training_data.message import Message
|
|
29
|
+
from rasa.shared.nlu.training_data.training_data import TrainingData
|
|
28
30
|
|
|
29
31
|
BUFFER_SLOTS_PREFIX = "buf_"
|
|
30
32
|
|
|
@@ -688,6 +690,31 @@ class CountVectorsFeaturizer(SparseFeaturizer, GraphComponent):
|
|
|
688
690
|
"""Check if any model got trained."""
|
|
689
691
|
return any(value is not None for value in attribute_vocabularies.values())
|
|
690
692
|
|
|
693
|
+
@staticmethod
|
|
694
|
+
def convert_vocab(
|
|
695
|
+
vocab: Dict[str, Union[int, Optional[Dict[str, int]]]], to_int: bool
|
|
696
|
+
) -> Dict[str, Union[None, int, np.int64, Dict[str, Union[int, np.int64]]]]:
|
|
697
|
+
"""Converts numpy integers in the vocabulary to Python integers."""
|
|
698
|
+
|
|
699
|
+
def convert_value(value: int) -> Union[int, np.int64]:
|
|
700
|
+
"""Helper function to convert a single value based on to_int flag."""
|
|
701
|
+
return int(value) if to_int else np.int64(value)
|
|
702
|
+
|
|
703
|
+
result_dict: Dict[
|
|
704
|
+
str, Union[None, int, np.int64, Dict[str, Union[int, np.int64]]]
|
|
705
|
+
] = {}
|
|
706
|
+
for key, sub_dict in vocab.items():
|
|
707
|
+
if isinstance(sub_dict, int):
|
|
708
|
+
result_dict[key] = convert_value(sub_dict)
|
|
709
|
+
elif not sub_dict:
|
|
710
|
+
result_dict[key] = None
|
|
711
|
+
else:
|
|
712
|
+
result_dict[key] = {
|
|
713
|
+
sub_key: convert_value(value) for sub_key, value in sub_dict.items()
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
return result_dict
|
|
717
|
+
|
|
691
718
|
def persist(self) -> None:
|
|
692
719
|
"""Persist this model into the passed directory.
|
|
693
720
|
|
|
@@ -701,17 +728,18 @@ class CountVectorsFeaturizer(SparseFeaturizer, GraphComponent):
|
|
|
701
728
|
attribute_vocabularies = self._collect_vectorizer_vocabularies()
|
|
702
729
|
if self._is_any_model_trained(attribute_vocabularies):
|
|
703
730
|
# Definitely need to persist some vocabularies
|
|
704
|
-
featurizer_file = model_dir / "vocabularies.
|
|
731
|
+
featurizer_file = model_dir / "vocabularies.json"
|
|
705
732
|
|
|
706
733
|
# Only persist vocabulary from one attribute if `use_shared_vocab`.
|
|
707
734
|
# Can be loaded and distributed to all attributes.
|
|
708
|
-
|
|
735
|
+
loaded_vocab = (
|
|
709
736
|
attribute_vocabularies[TEXT]
|
|
710
737
|
if self.use_shared_vocab
|
|
711
738
|
else attribute_vocabularies
|
|
712
739
|
)
|
|
740
|
+
vocab = self.convert_vocab(loaded_vocab, to_int=True)
|
|
713
741
|
|
|
714
|
-
|
|
742
|
+
rasa.shared.utils.io.dump_obj_as_json_to_file(featurizer_file, vocab)
|
|
715
743
|
|
|
716
744
|
# Dump OOV words separately as they might have been modified during
|
|
717
745
|
# training
|
|
@@ -786,8 +814,9 @@ class CountVectorsFeaturizer(SparseFeaturizer, GraphComponent):
|
|
|
786
814
|
"""Loads trained component (see parent class for full docstring)."""
|
|
787
815
|
try:
|
|
788
816
|
with model_storage.read_from(resource) as model_dir:
|
|
789
|
-
featurizer_file = model_dir / "vocabularies.
|
|
790
|
-
vocabulary =
|
|
817
|
+
featurizer_file = model_dir / "vocabularies.json"
|
|
818
|
+
vocabulary = rasa.shared.utils.io.read_json_file(featurizer_file)
|
|
819
|
+
vocabulary = cls.convert_vocab(vocabulary, to_int=False)
|
|
791
820
|
|
|
792
821
|
share_vocabulary = config["use_shared_vocab"]
|
|
793
822
|
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
2
3
|
import logging
|
|
3
4
|
from collections import OrderedDict
|
|
4
|
-
|
|
5
|
-
import scipy.sparse
|
|
6
|
-
import numpy as np
|
|
7
5
|
from typing import (
|
|
8
6
|
Any,
|
|
9
7
|
Dict,
|
|
@@ -17,30 +15,34 @@ from typing import (
|
|
|
17
15
|
Union,
|
|
18
16
|
)
|
|
19
17
|
|
|
18
|
+
import numpy as np
|
|
19
|
+
import scipy.sparse
|
|
20
|
+
|
|
21
|
+
import rasa.shared.utils.io
|
|
22
|
+
import rasa.utils.io
|
|
20
23
|
from rasa.engine.graph import ExecutionContext, GraphComponent
|
|
21
24
|
from rasa.engine.recipes.default_recipe import DefaultV1Recipe
|
|
22
25
|
from rasa.engine.storage.resource import Resource
|
|
23
26
|
from rasa.engine.storage.storage import ModelStorage
|
|
27
|
+
from rasa.nlu.constants import TOKENS_NAMES
|
|
28
|
+
from rasa.nlu.featurizers.sparse_featurizer.sparse_featurizer import SparseFeaturizer
|
|
24
29
|
from rasa.nlu.tokenizers.spacy_tokenizer import POS_TAG_KEY, SpacyTokenizer
|
|
25
30
|
from rasa.nlu.tokenizers.tokenizer import Token, Tokenizer
|
|
26
|
-
from rasa.nlu.featurizers.sparse_featurizer.sparse_featurizer import SparseFeaturizer
|
|
27
|
-
from rasa.nlu.constants import TOKENS_NAMES
|
|
28
31
|
from rasa.shared.constants import DOCS_URL_COMPONENTS
|
|
29
|
-
from rasa.shared.nlu.training_data.training_data import TrainingData
|
|
30
|
-
from rasa.shared.nlu.training_data.message import Message
|
|
31
|
-
from rasa.shared.nlu.constants import TEXT
|
|
32
32
|
from rasa.shared.exceptions import InvalidConfigException
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
from rasa.shared.nlu.constants import TEXT
|
|
34
|
+
from rasa.shared.nlu.training_data.message import Message
|
|
35
|
+
from rasa.shared.nlu.training_data.training_data import TrainingData
|
|
35
36
|
|
|
36
37
|
logger = logging.getLogger(__name__)
|
|
37
38
|
|
|
38
|
-
|
|
39
39
|
END_OF_SENTENCE = "EOS"
|
|
40
40
|
BEGIN_OF_SENTENCE = "BOS"
|
|
41
41
|
|
|
42
42
|
FEATURES = "features"
|
|
43
43
|
|
|
44
|
+
SEPERATOR = "###"
|
|
45
|
+
|
|
44
46
|
|
|
45
47
|
@DefaultV1Recipe.register(
|
|
46
48
|
DefaultV1Recipe.ComponentType.MESSAGE_FEATURIZER, is_trainable=True
|
|
@@ -72,7 +74,7 @@ class LexicalSyntacticFeaturizer(SparseFeaturizer, GraphComponent):
|
|
|
72
74
|
of the token at position `t+1`.
|
|
73
75
|
"""
|
|
74
76
|
|
|
75
|
-
FILENAME_FEATURE_TO_IDX_DICT = "feature_to_idx_dict.
|
|
77
|
+
FILENAME_FEATURE_TO_IDX_DICT = "feature_to_idx_dict.json"
|
|
76
78
|
|
|
77
79
|
# NOTE: "suffix5" of the token "is" will be "is". Hence, when combining multiple
|
|
78
80
|
# prefixes, short words will be represented/encoded repeatedly.
|
|
@@ -488,6 +490,32 @@ class LexicalSyntacticFeaturizer(SparseFeaturizer, GraphComponent):
|
|
|
488
490
|
"""Creates a new untrained component (see parent class for full docstring)."""
|
|
489
491
|
return cls(config, model_storage, resource, execution_context)
|
|
490
492
|
|
|
493
|
+
@staticmethod
|
|
494
|
+
def _restructure_feature_to_idx_dict(
|
|
495
|
+
loaded_data: Dict[str, Dict[str, int]],
|
|
496
|
+
) -> Dict[Tuple[int, str], Dict[str, int]]:
|
|
497
|
+
"""Reconstructs the feature to idx dict.
|
|
498
|
+
|
|
499
|
+
When storing the feature_to_idx_dict to disk, we need to convert the tuple (key)
|
|
500
|
+
into a string to be able to store it via json. When loading the data
|
|
501
|
+
we need to reconstruct the tuple from the stored string.
|
|
502
|
+
|
|
503
|
+
Args:
|
|
504
|
+
loaded_data: The loaded feature to idx dict from file.
|
|
505
|
+
|
|
506
|
+
Returns:
|
|
507
|
+
The reconstructed feature_to_idx_dict
|
|
508
|
+
"""
|
|
509
|
+
feature_to_idx_dict = {}
|
|
510
|
+
for tuple_string, feature_value in loaded_data.items():
|
|
511
|
+
# Example of tuple_string: "1###low"
|
|
512
|
+
index, feature_name = tuple_string.split(SEPERATOR)
|
|
513
|
+
|
|
514
|
+
feature_key = (int(index), feature_name)
|
|
515
|
+
feature_to_idx_dict[feature_key] = feature_value
|
|
516
|
+
|
|
517
|
+
return feature_to_idx_dict
|
|
518
|
+
|
|
491
519
|
@classmethod
|
|
492
520
|
def load(
|
|
493
521
|
cls,
|
|
@@ -500,10 +528,13 @@ class LexicalSyntacticFeaturizer(SparseFeaturizer, GraphComponent):
|
|
|
500
528
|
"""Loads trained component (see parent class for full docstring)."""
|
|
501
529
|
try:
|
|
502
530
|
with model_storage.read_from(resource) as model_path:
|
|
503
|
-
|
|
531
|
+
loaded_data = rasa.shared.utils.io.read_json_file(
|
|
504
532
|
model_path / cls.FILENAME_FEATURE_TO_IDX_DICT,
|
|
505
|
-
encode_non_string_keys=True,
|
|
506
533
|
)
|
|
534
|
+
|
|
535
|
+
# convert the key back into tuple
|
|
536
|
+
feature_to_idx_dict = cls._restructure_feature_to_idx_dict(loaded_data)
|
|
537
|
+
|
|
507
538
|
return cls(
|
|
508
539
|
config=config,
|
|
509
540
|
model_storage=model_storage,
|
|
@@ -528,9 +559,13 @@ class LexicalSyntacticFeaturizer(SparseFeaturizer, GraphComponent):
|
|
|
528
559
|
if not self._feature_to_idx_dict:
|
|
529
560
|
return None
|
|
530
561
|
|
|
562
|
+
# as we cannot dump tuples, convert the tuple into a string
|
|
563
|
+
restructured_feature_dict = {
|
|
564
|
+
f"{k[0]}{SEPERATOR}{k[1]}": v for k, v in self._feature_to_idx_dict.items()
|
|
565
|
+
}
|
|
566
|
+
|
|
531
567
|
with self._model_storage.write_to(self._resource) as model_path:
|
|
532
|
-
rasa.utils.io.
|
|
568
|
+
rasa.shared.utils.io.dump_obj_as_json_to_file(
|
|
533
569
|
model_path / self.FILENAME_FEATURE_TO_IDX_DICT,
|
|
534
|
-
|
|
535
|
-
encode_non_string_keys=True,
|
|
570
|
+
restructured_feature_dict,
|
|
536
571
|
)
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
2
3
|
import logging
|
|
3
4
|
import re
|
|
4
5
|
from typing import Any, Dict, List, Optional, Text, Tuple, Type
|
|
6
|
+
|
|
5
7
|
import numpy as np
|
|
6
8
|
import scipy.sparse
|
|
7
|
-
from rasa.nlu.tokenizers.tokenizer import Tokenizer
|
|
8
9
|
|
|
10
|
+
from rasa.nlu.tokenizers.tokenizer import Tokenizer
|
|
9
11
|
import rasa.shared.utils.io
|
|
10
12
|
import rasa.utils.io
|
|
11
13
|
import rasa.nlu.utils.pattern_utils as pattern_utils
|
|
@@ -240,7 +242,7 @@ class RegexFeaturizer(SparseFeaturizer, GraphComponent):
|
|
|
240
242
|
|
|
241
243
|
try:
|
|
242
244
|
with model_storage.read_from(resource) as model_dir:
|
|
243
|
-
patterns_file_name = model_dir / "patterns.
|
|
245
|
+
patterns_file_name = model_dir / "patterns.json"
|
|
244
246
|
known_patterns = rasa.shared.utils.io.read_json_file(patterns_file_name)
|
|
245
247
|
except (ValueError, FileNotFoundError):
|
|
246
248
|
logger.warning(
|
|
@@ -258,7 +260,7 @@ class RegexFeaturizer(SparseFeaturizer, GraphComponent):
|
|
|
258
260
|
|
|
259
261
|
def _persist(self) -> None:
|
|
260
262
|
with self._model_storage.write_to(self._resource) as model_dir:
|
|
261
|
-
regex_file = model_dir / "patterns.
|
|
263
|
+
regex_file = model_dir / "patterns.json"
|
|
262
264
|
rasa.shared.utils.io.dump_obj_as_json_to_file(
|
|
263
265
|
regex_file, self.known_patterns
|
|
264
266
|
)
|
rasa/shared/constants.py
CHANGED
|
@@ -183,7 +183,34 @@ STREAM_CONFIG_KEY = "stream"
|
|
|
183
183
|
N_REPHRASES_CONFIG_KEY = "n"
|
|
184
184
|
USE_CHAT_COMPLETIONS_ENDPOINT_CONFIG_KEY = "use_chat_completions_endpoint"
|
|
185
185
|
|
|
186
|
+
ROUTER_CONFIG_KEY = "router"
|
|
187
|
+
ROUTER_STRATEGY_CONFIG_KEY = "router_strategy"
|
|
188
|
+
REDIS_HOST_CONFIG_KEY = "redis_host"
|
|
189
|
+
ROUTER_STRATEGIES_REQUIRING_REDIS_CACHE = [
|
|
190
|
+
"cost-based-routing",
|
|
191
|
+
"usage-based-routing",
|
|
192
|
+
]
|
|
193
|
+
ROUTER_STRATEGIES_NOT_REQUIRING_CACHE = [
|
|
194
|
+
"latency-based-routing",
|
|
195
|
+
"least-busy",
|
|
196
|
+
"simple-shuffle",
|
|
197
|
+
]
|
|
198
|
+
VALID_ROUTER_STRATEGIES = (
|
|
199
|
+
ROUTER_STRATEGIES_REQUIRING_REDIS_CACHE + ROUTER_STRATEGIES_NOT_REQUIRING_CACHE
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
MODELS_CONFIG_KEY = "models"
|
|
203
|
+
MODEL_GROUPS_CONFIG_KEY = "model_groups"
|
|
204
|
+
MODEL_GROUP_CONFIG_KEY = "model_group"
|
|
205
|
+
MODEL_GROUP_ID_CONFIG_KEY = "id"
|
|
206
|
+
|
|
207
|
+
EXTRA_PARAMETERS_KEY = "extra_parameters"
|
|
208
|
+
MODEL_GROUP_ID_KEY = "model_group_id"
|
|
209
|
+
MODEL_LIST_KEY = "model_list"
|
|
210
|
+
LITELLM_PARAMS_KEY = "litellm_params"
|
|
211
|
+
|
|
186
212
|
LLM_API_HEALTH_CHECK_ENV_VAR = "LLM_API_HEALTH_CHECK"
|
|
213
|
+
LLM_API_HEALTH_CHECK_DEFAULT_VALUE = "false"
|
|
187
214
|
|
|
188
215
|
AZURE_API_KEY_ENV_VAR = "AZURE_API_KEY"
|
|
189
216
|
AZURE_AD_TOKEN_ENV_VAR = "AZURE_AD_TOKEN"
|
|
@@ -191,6 +218,8 @@ AZURE_API_BASE_ENV_VAR = "AZURE_API_BASE"
|
|
|
191
218
|
AZURE_API_VERSION_ENV_VAR = "AZURE_API_VERSION"
|
|
192
219
|
AZURE_API_TYPE_ENV_VAR = "AZURE_API_TYPE"
|
|
193
220
|
|
|
221
|
+
AWS_REGION_NAME_CONFIG_KEY = "aws_region_name"
|
|
222
|
+
|
|
194
223
|
HUGGINGFACE_MULTIPROCESS_CONFIG_KEY = "multi_process"
|
|
195
224
|
HUGGINGFACE_CACHE_FOLDER_CONFIG_KEY = "cache_folder"
|
|
196
225
|
HUGGINGFACE_SHOW_PROGRESS_CONFIG_KEY = "show_progress"
|
|
@@ -211,6 +240,13 @@ OPENAI_PROVIDER = "openai"
|
|
|
211
240
|
AZURE_OPENAI_PROVIDER = "azure"
|
|
212
241
|
SELF_HOSTED_PROVIDER = "self-hosted"
|
|
213
242
|
HUGGINGFACE_LOCAL_EMBEDDING_PROVIDER = "huggingface_local"
|
|
243
|
+
RASA_PROVIDER = "rasa"
|
|
244
|
+
|
|
245
|
+
SELF_HOSTED_VLLM_PREFIX = "hosted_vllm"
|
|
246
|
+
SELF_HOSTED_VLLM_API_KEY_ENV_VAR = "HOSTED_VLLM_API_KEY"
|
|
247
|
+
|
|
248
|
+
SELF_HOSTED_VLLM_PREFIX = "hosted_vllm"
|
|
249
|
+
SELF_HOSTED_VLLM_API_KEY_ENV_VAR = "HOSTED_VLLM_API_KEY"
|
|
214
250
|
|
|
215
251
|
SELF_HOSTED_VLLM_PREFIX = "hosted_vllm"
|
|
216
252
|
SELF_HOSTED_VLLM_API_KEY_ENV_VAR = "HOSTED_VLLM_API_KEY"
|
|
@@ -244,6 +280,3 @@ RASA_PATTERN_CANNOT_HANDLE_INVALID_INTENT = (
|
|
|
244
280
|
)
|
|
245
281
|
|
|
246
282
|
ROUTE_TO_CALM_SLOT = "route_session_to_calm"
|
|
247
|
-
|
|
248
|
-
ORIGINAL_VALUE = "original_value"
|
|
249
|
-
RESOLVED_VALUE = "resolved_value"
|
rasa/shared/core/constants.py
CHANGED
|
@@ -11,6 +11,7 @@ USER_INTENT_BACK = "back"
|
|
|
11
11
|
USER_INTENT_OUT_OF_SCOPE = "out_of_scope"
|
|
12
12
|
USER_INTENT_SESSION_START = "session_start"
|
|
13
13
|
USER_INTENT_SESSION_END = "session_end"
|
|
14
|
+
USER_INTENT_SILENCE_TIMEOUT = "silence_timeout"
|
|
14
15
|
SESSION_START_METADATA_SLOT = "session_started_metadata"
|
|
15
16
|
|
|
16
17
|
DEFAULT_INTENTS = [
|
|
@@ -19,6 +20,7 @@ DEFAULT_INTENTS = [
|
|
|
19
20
|
USER_INTENT_OUT_OF_SCOPE,
|
|
20
21
|
USER_INTENT_SESSION_START,
|
|
21
22
|
USER_INTENT_SESSION_END,
|
|
23
|
+
USER_INTENT_SILENCE_TIMEOUT,
|
|
22
24
|
constants.DEFAULT_NLU_FALLBACK_INTENT_NAME,
|
|
23
25
|
]
|
|
24
26
|
|
|
@@ -106,6 +108,11 @@ FLOW_HASHES_SLOT = "flow_hashes"
|
|
|
106
108
|
|
|
107
109
|
FLOW_SLOT_NAMES = [FLOW_HASHES_SLOT]
|
|
108
110
|
|
|
111
|
+
# slots for audio timeout
|
|
112
|
+
SLOT_SILENCE_TIMEOUT = "silence_timeout"
|
|
113
|
+
SILENCE_TIMEOUT_DEFAULT_VALUE = 6.0
|
|
114
|
+
SLOT_CONSECUTIVE_SILENCE_TIMEOUTS = "consecutive_silence_timeouts"
|
|
115
|
+
SILENCE_SLOTS = [SLOT_SILENCE_TIMEOUT, SLOT_CONSECUTIVE_SILENCE_TIMEOUTS]
|
|
109
116
|
# slots for knowledge base
|
|
110
117
|
SLOT_LISTED_ITEMS = "knowledge_base_listed_objects"
|
|
111
118
|
SLOT_LAST_OBJECT = "knowledge_base_last_object"
|
rasa/shared/core/domain.py
CHANGED
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import collections
|
|
4
4
|
import copy
|
|
5
5
|
import json
|
|
6
|
+
import math
|
|
6
7
|
import os
|
|
7
8
|
from dataclasses import dataclass
|
|
8
9
|
from functools import cached_property
|
|
@@ -57,6 +58,7 @@ from rasa.shared.core.events import SlotSet, UserUttered
|
|
|
57
58
|
from rasa.shared.core.slots import (
|
|
58
59
|
AnySlot,
|
|
59
60
|
CategoricalSlot,
|
|
61
|
+
FloatSlot,
|
|
60
62
|
ListSlot,
|
|
61
63
|
Slot,
|
|
62
64
|
TextSlot,
|
|
@@ -1082,6 +1084,7 @@ class Domain:
|
|
|
1082
1084
|
self._add_knowledge_base_slots()
|
|
1083
1085
|
self._add_categorical_slot_default_value()
|
|
1084
1086
|
self._add_session_metadata_slot()
|
|
1087
|
+
self._add_audio_slots()
|
|
1085
1088
|
|
|
1086
1089
|
def _add_categorical_slot_default_value(self) -> None:
|
|
1087
1090
|
"""Add a default value to all categorical slots.
|
|
@@ -1136,6 +1139,29 @@ class Domain:
|
|
|
1136
1139
|
)
|
|
1137
1140
|
)
|
|
1138
1141
|
|
|
1142
|
+
def _add_audio_slots(self) -> None:
|
|
1143
|
+
"""Add slots relevant for audio channels."""
|
|
1144
|
+
self.slots.append(
|
|
1145
|
+
FloatSlot(
|
|
1146
|
+
rasa.shared.core.constants.SLOT_SILENCE_TIMEOUT,
|
|
1147
|
+
mappings=[],
|
|
1148
|
+
influence_conversation=False,
|
|
1149
|
+
is_builtin=True,
|
|
1150
|
+
initial_value=rasa.shared.core.constants.SILENCE_TIMEOUT_DEFAULT_VALUE,
|
|
1151
|
+
max_value=math.inf,
|
|
1152
|
+
)
|
|
1153
|
+
)
|
|
1154
|
+
self.slots.append(
|
|
1155
|
+
FloatSlot(
|
|
1156
|
+
rasa.shared.core.constants.SLOT_CONSECUTIVE_SILENCE_TIMEOUTS,
|
|
1157
|
+
mappings=[],
|
|
1158
|
+
influence_conversation=False,
|
|
1159
|
+
is_builtin=True,
|
|
1160
|
+
initial_value=0.0,
|
|
1161
|
+
max_value=math.inf,
|
|
1162
|
+
)
|
|
1163
|
+
)
|
|
1164
|
+
|
|
1139
1165
|
def _add_knowledge_base_slots(self) -> None:
|
|
1140
1166
|
"""Add slots for the knowledge base action to slots.
|
|
1141
1167
|
|
rasa/shared/core/flows/flow.py
CHANGED
|
@@ -60,6 +60,8 @@ class Flow:
|
|
|
60
60
|
"""
|
|
61
61
|
file_path: Optional[str] = None
|
|
62
62
|
"""The path to the file where the flow is stored."""
|
|
63
|
+
persisted_slots: List[str] = field(default_factory=list)
|
|
64
|
+
"""The list of slots that should be persisted after the flow ends."""
|
|
63
65
|
|
|
64
66
|
@staticmethod
|
|
65
67
|
def from_json(
|
|
@@ -95,6 +97,7 @@ class Flow:
|
|
|
95
97
|
# If we are reading the flows in after training the file_path is part of
|
|
96
98
|
# data. When the model is trained, take the provided file_path.
|
|
97
99
|
file_path=data.get("file_path") if "file_path" in data else file_path,
|
|
100
|
+
persisted_slots=data.get("persisted_slots", []),
|
|
98
101
|
)
|
|
99
102
|
|
|
100
103
|
def get_full_name(self) -> str:
|
|
@@ -167,6 +170,8 @@ class Flow:
|
|
|
167
170
|
data["nlu_trigger"] = self.nlu_triggers.as_json()
|
|
168
171
|
if self.file_path:
|
|
169
172
|
data["file_path"] = self.file_path
|
|
173
|
+
if self.persisted_slots:
|
|
174
|
+
data["persisted_slots"] = self.persisted_slots
|
|
170
175
|
|
|
171
176
|
return data
|
|
172
177
|
|
|
@@ -279,6 +279,9 @@
|
|
|
279
279
|
},
|
|
280
280
|
"steps": {
|
|
281
281
|
"$ref": "#/$defs/steps"
|
|
282
|
+
},
|
|
283
|
+
"persisted_slots": {
|
|
284
|
+
"$ref": "#/$defs/persisted_slots"
|
|
282
285
|
}
|
|
283
286
|
}
|
|
284
287
|
},
|
|
@@ -288,6 +291,13 @@
|
|
|
288
291
|
"items": {
|
|
289
292
|
"type": "object"
|
|
290
293
|
}
|
|
294
|
+
},
|
|
295
|
+
"persisted_slots": {
|
|
296
|
+
"type": "array",
|
|
297
|
+
"schema_name": "list of slots",
|
|
298
|
+
"items": {
|
|
299
|
+
"type": "string"
|
|
300
|
+
}
|
|
291
301
|
}
|
|
292
302
|
}
|
|
293
303
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from typing import Set
|
|
2
|
+
from rasa.shared.utils.io import raise_deprecation_warning
|
|
3
|
+
|
|
4
|
+
RESET_PROPERTY_NAME = "reset_after_flow_ends"
|
|
5
|
+
PERSIST_PROPERTY_NAME = "persisted_slots"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def warn_deprecated_collect_step_config(flow_id: str, collect_step: str) -> None:
|
|
9
|
+
"""Warns about deprecated reset_after_flow_ends usage in collect steps."""
|
|
10
|
+
raise_deprecation_warning(
|
|
11
|
+
f"Configuring '{RESET_PROPERTY_NAME}' in collect step '{collect_step}' is "
|
|
12
|
+
f"deprecated and will be removed in Rasa Pro 4.0.0. In flow id '{flow_id}', "
|
|
13
|
+
f"please use the '{PERSIST_PROPERTY_NAME}' "
|
|
14
|
+
"property at the flow level instead."
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_duplicate_slot_persistence_config_error_message(
|
|
19
|
+
flow_id: str, collect_step: str
|
|
20
|
+
) -> str:
|
|
21
|
+
"""Returns an error message for duplicate slot persistence configuration."""
|
|
22
|
+
return (
|
|
23
|
+
f"Flow with id '{flow_id}' uses the '{RESET_PROPERTY_NAME}' property "
|
|
24
|
+
f"in collect step '{collect_step}' and also the "
|
|
25
|
+
f"'{PERSIST_PROPERTY_NAME}' property at the flow level. "
|
|
26
|
+
"Please use only one of the two configuration methods."
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_invalid_slot_persistence_config_error_message(
|
|
31
|
+
flow_id: str, invalid_slots: Set[str]
|
|
32
|
+
) -> str:
|
|
33
|
+
"""Returns an error message for invalid slot persistence configuration."""
|
|
34
|
+
return (
|
|
35
|
+
f"Flow with id '{flow_id}' lists slot(s) '{invalid_slots}' in the "
|
|
36
|
+
f"'{PERSIST_PROPERTY_NAME}' property. However these slots "
|
|
37
|
+
f"are neither used in a collect step nor a set_slot step of the flow. "
|
|
38
|
+
f"Please remove such slots from the '{PERSIST_PROPERTY_NAME}' property."
|
|
39
|
+
)
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import re
|
|
4
4
|
import typing
|
|
5
5
|
from collections import defaultdict
|
|
6
|
+
from dataclasses import dataclass
|
|
6
7
|
from typing import Optional, Set, Text, List
|
|
7
8
|
|
|
8
9
|
from rasa.shared.constants import (
|
|
@@ -26,6 +27,12 @@ from rasa.shared.core.flows.steps.call import CallFlowStep
|
|
|
26
27
|
from rasa.shared.core.flows.steps.collect import CollectInformationFlowStep
|
|
27
28
|
from rasa.shared.core.flows.steps.constants import CONTINUE_STEP_PREFIX, DEFAULT_STEPS
|
|
28
29
|
from rasa.shared.core.flows.steps.link import LinkFlowStep
|
|
30
|
+
from rasa.shared.core.flows.steps.set_slots import SetSlotsFlowStep
|
|
31
|
+
from rasa.shared.core.flows.utils import (
|
|
32
|
+
warn_deprecated_collect_step_config,
|
|
33
|
+
get_duplicate_slot_persistence_config_error_message,
|
|
34
|
+
get_invalid_slot_persistence_config_error_message,
|
|
35
|
+
)
|
|
29
36
|
from rasa.shared.exceptions import RasaException
|
|
30
37
|
|
|
31
38
|
if typing.TYPE_CHECKING:
|
|
@@ -379,6 +386,42 @@ class FlowIdNamingException(RasaException):
|
|
|
379
386
|
)
|
|
380
387
|
|
|
381
388
|
|
|
389
|
+
class DuplicateSlotPersistConfigException(RasaException):
|
|
390
|
+
"""Raised when a slot persist configuration is duplicated."""
|
|
391
|
+
|
|
392
|
+
def __init__(self, flow_id: str, collect_step: str) -> None:
|
|
393
|
+
"""Initializes the exception."""
|
|
394
|
+
self.flow_id = flow_id
|
|
395
|
+
self.collect_step = collect_step
|
|
396
|
+
|
|
397
|
+
def __str__(self) -> str:
|
|
398
|
+
"""Return a string representation of the exception."""
|
|
399
|
+
return get_duplicate_slot_persistence_config_error_message(
|
|
400
|
+
self.flow_id, self.collect_step
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
class InvalidPersistSlotsException(RasaException):
|
|
405
|
+
"""Raised when a slot persist configuration is duplicated."""
|
|
406
|
+
|
|
407
|
+
def __init__(self, flow_id: str, invalid_slots: Set[str]) -> None:
|
|
408
|
+
"""Initializes the exception."""
|
|
409
|
+
self.flow_id = flow_id
|
|
410
|
+
self.invalid_slots = invalid_slots
|
|
411
|
+
|
|
412
|
+
def __str__(self) -> str:
|
|
413
|
+
"""Return a string representation of the exception."""
|
|
414
|
+
return get_invalid_slot_persistence_config_error_message(
|
|
415
|
+
self.flow_id, self.invalid_slots
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
@dataclass
|
|
420
|
+
class ValidationResult:
|
|
421
|
+
is_valid: bool
|
|
422
|
+
invalid_slots: Set[str]
|
|
423
|
+
|
|
424
|
+
|
|
382
425
|
def validate_flow(flow: Flow) -> None:
|
|
383
426
|
"""Validates the flow configuration.
|
|
384
427
|
|
|
@@ -387,6 +430,8 @@ def validate_flow(flow: Flow) -> None:
|
|
|
387
430
|
- whether all next links point to existing steps
|
|
388
431
|
- whether all steps can be reached from the start step
|
|
389
432
|
"""
|
|
433
|
+
from rasa.cli.utils import is_skip_validation_flag_set
|
|
434
|
+
|
|
390
435
|
validate_flow_not_empty(flow)
|
|
391
436
|
validate_no_empty_step_sequences(flow)
|
|
392
437
|
validate_all_steps_next_property(flow)
|
|
@@ -397,6 +442,12 @@ def validate_flow(flow: Flow) -> None:
|
|
|
397
442
|
validate_slot_names_to_be_collected(flow)
|
|
398
443
|
validate_flow_id(flow)
|
|
399
444
|
|
|
445
|
+
if is_skip_validation_flag_set():
|
|
446
|
+
# we only want to run this validation if the --skip-validation flag is used
|
|
447
|
+
# during training because Flow Validation exceptions are raised one by one
|
|
448
|
+
# as opposed to all at once with the Validator class
|
|
449
|
+
validate_slot_persistence_configuration(flow)
|
|
450
|
+
|
|
400
451
|
|
|
401
452
|
def validate_flow_not_empty(flow: Flow) -> None:
|
|
402
453
|
"""Validate that the flow is not empty."""
|
|
@@ -637,3 +688,48 @@ def validate_flow_id(flow: Flow) -> None:
|
|
|
637
688
|
flow_re = re.compile(FLOW_ID_REGEX)
|
|
638
689
|
if not flow_re.search(flow.id):
|
|
639
690
|
raise FlowIdNamingException(flow.id)
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
def validate_slot_persistence_configuration(flow: Flow) -> None:
|
|
694
|
+
"""Validates that slot persistence configuration is valid.
|
|
695
|
+
|
|
696
|
+
Only slots used in either a collect step or a set_slot step can be persisted
|
|
697
|
+
and the configuration can either be set at the flow level or the collect step level,
|
|
698
|
+
but not both.
|
|
699
|
+
|
|
700
|
+
Args:
|
|
701
|
+
flow: The flow to validate.
|
|
702
|
+
|
|
703
|
+
Raises:
|
|
704
|
+
DuplicateSlotPersistConfigException: If slot persist config is duplicated.
|
|
705
|
+
"""
|
|
706
|
+
|
|
707
|
+
def _is_persist_slots_valid(
|
|
708
|
+
persist_slots: List[str], flow_slots: Set[str]
|
|
709
|
+
) -> ValidationResult:
|
|
710
|
+
"""Validates that the slots that should be persisted are used in the flow."""
|
|
711
|
+
invalid_slots = set(persist_slots) - flow_slots
|
|
712
|
+
is_valid = False if invalid_slots else True
|
|
713
|
+
|
|
714
|
+
return ValidationResult(is_valid, invalid_slots)
|
|
715
|
+
|
|
716
|
+
flow_id = flow.id
|
|
717
|
+
persist_slots = flow.persisted_slots
|
|
718
|
+
has_flow_level_persistence = True if persist_slots else False
|
|
719
|
+
flow_slots = set()
|
|
720
|
+
|
|
721
|
+
for step in flow.steps_with_calls_resolved:
|
|
722
|
+
if isinstance(step, SetSlotsFlowStep):
|
|
723
|
+
flow_slots.update([slot["key"] for slot in step.slots])
|
|
724
|
+
elif isinstance(step, CollectInformationFlowStep):
|
|
725
|
+
flow_slots.add(step.collect)
|
|
726
|
+
if not step.reset_after_flow_ends:
|
|
727
|
+
collect_step = step.collect
|
|
728
|
+
warn_deprecated_collect_step_config(flow_id, collect_step)
|
|
729
|
+
if has_flow_level_persistence:
|
|
730
|
+
raise DuplicateSlotPersistConfigException(flow_id, collect_step)
|
|
731
|
+
|
|
732
|
+
if has_flow_level_persistence:
|
|
733
|
+
result = _is_persist_slots_valid(persist_slots, flow_slots)
|
|
734
|
+
if not result.is_valid:
|
|
735
|
+
raise InvalidPersistSlotsException(flow_id, result.invalid_slots)
|
rasa/shared/core/slots.py
CHANGED
|
@@ -193,6 +193,11 @@ class Slot(ABC):
|
|
|
193
193
|
data.update(self.persistence_info())
|
|
194
194
|
return rasa.shared.utils.io.get_dictionary_fingerprint(data)
|
|
195
195
|
|
|
196
|
+
def __eq__(self, other: Any) -> bool:
|
|
197
|
+
if not isinstance(other, Slot):
|
|
198
|
+
return False
|
|
199
|
+
return self.name == other.name and self.value == other.value
|
|
200
|
+
|
|
196
201
|
|
|
197
202
|
class FloatSlot(Slot):
|
|
198
203
|
"""A slot storing a float value."""
|