rasa-pro 3.9.17__py3-none-any.whl → 3.10.3__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 +5 -37
- rasa/__init__.py +1 -2
- rasa/__main__.py +5 -0
- rasa/anonymization/anonymization_rule_executor.py +2 -2
- rasa/api.py +26 -22
- rasa/cli/arguments/data.py +27 -2
- rasa/cli/arguments/default_arguments.py +25 -3
- rasa/cli/arguments/run.py +9 -9
- rasa/cli/arguments/train.py +2 -0
- rasa/cli/data.py +70 -8
- rasa/cli/e2e_test.py +108 -433
- rasa/cli/interactive.py +1 -0
- rasa/cli/llm_fine_tuning.py +395 -0
- rasa/cli/project_templates/calm/endpoints.yml +1 -1
- rasa/cli/project_templates/tutorial/endpoints.yml +1 -1
- rasa/cli/run.py +14 -13
- rasa/cli/scaffold.py +10 -8
- rasa/cli/train.py +8 -7
- rasa/cli/utils.py +15 -0
- rasa/constants.py +7 -1
- rasa/core/actions/action.py +98 -49
- rasa/core/actions/action_run_slot_rejections.py +4 -1
- rasa/core/actions/custom_action_executor.py +9 -6
- rasa/core/actions/direct_custom_actions_executor.py +80 -0
- rasa/core/actions/e2e_stub_custom_action_executor.py +68 -0
- rasa/core/actions/grpc_custom_action_executor.py +2 -2
- rasa/core/actions/http_custom_action_executor.py +6 -5
- rasa/core/agent.py +21 -17
- rasa/core/channels/__init__.py +2 -0
- rasa/core/channels/audiocodes.py +1 -16
- rasa/core/channels/voice_aware/__init__.py +0 -0
- rasa/core/channels/voice_aware/jambonz.py +103 -0
- rasa/core/channels/voice_aware/jambonz_protocol.py +344 -0
- rasa/core/channels/voice_aware/utils.py +20 -0
- rasa/core/channels/voice_native/__init__.py +0 -0
- rasa/core/constants.py +6 -1
- rasa/core/featurizers/single_state_featurizer.py +1 -22
- rasa/core/featurizers/tracker_featurizers.py +18 -115
- rasa/core/information_retrieval/faiss.py +7 -4
- rasa/core/information_retrieval/information_retrieval.py +8 -0
- rasa/core/information_retrieval/milvus.py +9 -2
- rasa/core/information_retrieval/qdrant.py +1 -1
- rasa/core/nlg/contextual_response_rephraser.py +32 -10
- rasa/core/nlg/summarize.py +4 -3
- rasa/core/policies/enterprise_search_policy.py +100 -44
- rasa/core/policies/flows/flow_executor.py +155 -98
- rasa/core/policies/intentless_policy.py +52 -28
- rasa/core/policies/ted_policy.py +33 -58
- rasa/core/policies/unexpected_intent_policy.py +7 -15
- rasa/core/processor.py +15 -46
- rasa/core/run.py +5 -4
- rasa/core/tracker_store.py +8 -4
- rasa/core/utils.py +45 -56
- rasa/dialogue_understanding/coexistence/llm_based_router.py +45 -12
- rasa/dialogue_understanding/commands/__init__.py +4 -0
- rasa/dialogue_understanding/commands/change_flow_command.py +0 -6
- rasa/dialogue_understanding/commands/session_start_command.py +59 -0
- rasa/dialogue_understanding/commands/set_slot_command.py +1 -5
- rasa/dialogue_understanding/commands/utils.py +38 -0
- rasa/dialogue_understanding/generator/constants.py +10 -3
- rasa/dialogue_understanding/generator/flow_retrieval.py +14 -5
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +12 -2
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +106 -87
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +28 -6
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +90 -37
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +15 -15
- rasa/dialogue_understanding/patterns/session_start.py +37 -0
- rasa/dialogue_understanding/processor/command_processor.py +13 -14
- rasa/e2e_test/aggregate_test_stats_calculator.py +124 -0
- rasa/e2e_test/assertions.py +1181 -0
- rasa/e2e_test/assertions_schema.yml +106 -0
- rasa/e2e_test/constants.py +20 -0
- rasa/e2e_test/e2e_config.py +220 -0
- rasa/e2e_test/e2e_config_schema.yml +26 -0
- rasa/e2e_test/e2e_test_case.py +131 -8
- rasa/e2e_test/e2e_test_converter.py +363 -0
- rasa/e2e_test/e2e_test_converter_prompt.jinja2 +70 -0
- rasa/e2e_test/e2e_test_coverage_report.py +364 -0
- rasa/e2e_test/e2e_test_result.py +26 -6
- rasa/e2e_test/e2e_test_runner.py +498 -73
- rasa/e2e_test/e2e_test_schema.yml +96 -0
- rasa/e2e_test/pykwalify_extensions.py +39 -0
- rasa/e2e_test/stub_custom_action.py +70 -0
- rasa/e2e_test/utils/__init__.py +0 -0
- rasa/e2e_test/utils/e2e_yaml_utils.py +55 -0
- rasa/e2e_test/utils/io.py +596 -0
- rasa/e2e_test/utils/validation.py +80 -0
- rasa/engine/recipes/default_components.py +0 -2
- rasa/engine/storage/local_model_storage.py +0 -1
- rasa/env.py +9 -0
- rasa/llm_fine_tuning/__init__.py +0 -0
- rasa/llm_fine_tuning/annotation_module.py +241 -0
- rasa/llm_fine_tuning/conversations.py +144 -0
- rasa/llm_fine_tuning/llm_data_preparation_module.py +178 -0
- rasa/llm_fine_tuning/notebooks/unsloth_finetuning.ipynb +407 -0
- rasa/llm_fine_tuning/paraphrasing/__init__.py +0 -0
- rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +281 -0
- rasa/llm_fine_tuning/paraphrasing/default_rephrase_prompt_template.jina2 +44 -0
- rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +121 -0
- rasa/llm_fine_tuning/paraphrasing/rephrased_user_message.py +10 -0
- rasa/llm_fine_tuning/paraphrasing_module.py +128 -0
- rasa/llm_fine_tuning/storage.py +174 -0
- rasa/llm_fine_tuning/train_test_split_module.py +441 -0
- rasa/model_training.py +48 -16
- rasa/nlu/classifiers/diet_classifier.py +25 -38
- rasa/nlu/classifiers/logistic_regression_classifier.py +9 -44
- rasa/nlu/classifiers/sklearn_intent_classifier.py +16 -37
- rasa/nlu/extractors/crf_entity_extractor.py +50 -93
- rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +45 -78
- rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +17 -52
- rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +3 -5
- rasa/nlu/persistor.py +129 -32
- rasa/server.py +45 -10
- rasa/shared/constants.py +63 -15
- rasa/shared/core/domain.py +15 -12
- rasa/shared/core/events.py +28 -2
- rasa/shared/core/flows/flow.py +208 -13
- rasa/shared/core/flows/flow_path.py +84 -0
- rasa/shared/core/flows/flows_list.py +28 -10
- rasa/shared/core/flows/flows_yaml_schema.json +269 -193
- rasa/shared/core/flows/validation.py +112 -25
- rasa/shared/core/flows/yaml_flows_io.py +149 -10
- rasa/shared/core/trackers.py +6 -0
- rasa/shared/core/training_data/visualization.html +2 -2
- rasa/shared/exceptions.py +4 -0
- rasa/shared/importers/importer.py +60 -11
- rasa/shared/importers/remote_importer.py +196 -0
- rasa/shared/nlu/constants.py +2 -0
- rasa/shared/nlu/training_data/features.py +2 -120
- rasa/shared/providers/_configs/__init__.py +0 -0
- rasa/shared/providers/_configs/azure_openai_client_config.py +181 -0
- rasa/shared/providers/_configs/client_config.py +57 -0
- rasa/shared/providers/_configs/default_litellm_client_config.py +130 -0
- rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +234 -0
- rasa/shared/providers/_configs/openai_client_config.py +175 -0
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +171 -0
- rasa/shared/providers/_configs/utils.py +101 -0
- rasa/shared/providers/_ssl_verification_utils.py +124 -0
- rasa/shared/providers/embedding/__init__.py +0 -0
- rasa/shared/providers/embedding/_base_litellm_embedding_client.py +254 -0
- rasa/shared/providers/embedding/_langchain_embedding_client_adapter.py +74 -0
- rasa/shared/providers/embedding/azure_openai_embedding_client.py +277 -0
- rasa/shared/providers/embedding/default_litellm_embedding_client.py +102 -0
- rasa/shared/providers/embedding/embedding_client.py +90 -0
- rasa/shared/providers/embedding/embedding_response.py +41 -0
- rasa/shared/providers/embedding/huggingface_local_embedding_client.py +191 -0
- rasa/shared/providers/embedding/openai_embedding_client.py +172 -0
- rasa/shared/providers/llm/__init__.py +0 -0
- rasa/shared/providers/llm/_base_litellm_client.py +227 -0
- rasa/shared/providers/llm/azure_openai_llm_client.py +338 -0
- rasa/shared/providers/llm/default_litellm_llm_client.py +84 -0
- rasa/shared/providers/llm/llm_client.py +76 -0
- rasa/shared/providers/llm/llm_response.py +50 -0
- rasa/shared/providers/llm/openai_llm_client.py +155 -0
- rasa/shared/providers/llm/self_hosted_llm_client.py +169 -0
- rasa/shared/providers/mappings.py +75 -0
- rasa/shared/utils/cli.py +30 -0
- rasa/shared/utils/io.py +65 -3
- rasa/shared/utils/llm.py +223 -200
- rasa/shared/utils/yaml.py +122 -7
- rasa/studio/download.py +19 -13
- rasa/studio/train.py +2 -3
- rasa/studio/upload.py +2 -3
- rasa/telemetry.py +113 -58
- rasa/tracing/config.py +2 -3
- rasa/tracing/instrumentation/attribute_extractors.py +29 -17
- rasa/tracing/instrumentation/instrumentation.py +4 -47
- rasa/utils/common.py +18 -19
- rasa/utils/endpoints.py +7 -4
- rasa/utils/io.py +66 -0
- rasa/utils/json_utils.py +60 -0
- rasa/utils/licensing.py +9 -1
- rasa/utils/ml_utils.py +4 -2
- rasa/utils/tensorflow/model_data.py +193 -2
- rasa/validator.py +195 -1
- rasa/version.py +1 -1
- {rasa_pro-3.9.17.dist-info → rasa_pro-3.10.3.dist-info}/METADATA +25 -51
- {rasa_pro-3.9.17.dist-info → rasa_pro-3.10.3.dist-info}/RECORD +183 -119
- rasa/nlu/classifiers/llm_intent_classifier.py +0 -519
- rasa/shared/providers/openai/clients.py +0 -43
- rasa/shared/providers/openai/session_handler.py +0 -110
- rasa/utils/tensorflow/feature_array.py +0 -366
- /rasa/{shared/providers/openai → cli/project_templates/tutorial/actions}/__init__.py +0 -0
- /rasa/cli/project_templates/tutorial/{actions.py → actions/actions.py} +0 -0
- {rasa_pro-3.9.17.dist-info → rasa_pro-3.10.3.dist-info}/NOTICE +0 -0
- {rasa_pro-3.9.17.dist-info → rasa_pro-3.10.3.dist-info}/WHEEL +0 -0
- {rasa_pro-3.9.17.dist-info → rasa_pro-3.10.3.dist-info}/entry_points.txt +0 -0
rasa/utils/common.py
CHANGED
|
@@ -32,6 +32,7 @@ from rasa.constants import (
|
|
|
32
32
|
DEFAULT_LOG_LEVEL_LIBRARIES,
|
|
33
33
|
ENV_LOG_LEVEL_LIBRARIES,
|
|
34
34
|
ENV_LOG_LEVEL_MATPLOTLIB,
|
|
35
|
+
ENV_LOG_LEVEL_MLFLOW,
|
|
35
36
|
ENV_LOG_LEVEL_RABBITMQ,
|
|
36
37
|
ENV_LOG_LEVEL_KAFKA,
|
|
37
38
|
)
|
|
@@ -44,12 +45,6 @@ logger = logging.getLogger(__name__)
|
|
|
44
45
|
|
|
45
46
|
T = TypeVar("T")
|
|
46
47
|
|
|
47
|
-
EXPECTED_PILLOW_DEPRECATION_WARNINGS: List[Tuple[Type[Warning], str]] = [
|
|
48
|
-
# Keras uses deprecated Pillow features
|
|
49
|
-
# cf. https://github.com/keras-team/keras/issues/16639
|
|
50
|
-
(DeprecationWarning, f"{method} is deprecated and will be removed in Pillow 10 .*")
|
|
51
|
-
for method in ["BICUBIC", "NEAREST", "BILINEAR", "HAMMING", "BOX", "LANCZOS"]
|
|
52
|
-
]
|
|
53
48
|
|
|
54
49
|
EXPECTED_WARNINGS: List[Tuple[Type[Warning], str]] = [
|
|
55
50
|
# TODO (issue #9932)
|
|
@@ -57,22 +52,17 @@ EXPECTED_WARNINGS: List[Tuple[Type[Warning], str]] = [
|
|
|
57
52
|
np.VisibleDeprecationWarning,
|
|
58
53
|
"Creating an ndarray from ragged nested sequences.*",
|
|
59
54
|
),
|
|
60
|
-
# raised by langchain -> faiss
|
|
61
|
-
(
|
|
62
|
-
DeprecationWarning,
|
|
63
|
-
"distutils Version classes are deprecated. Use packaging.version instead",
|
|
64
|
-
),
|
|
65
55
|
# raised by pycountry (rasa-plus anonymization), magic_filter, google rpc
|
|
66
56
|
# and probably other dependencies that use pkg_resources instead of importlib
|
|
67
57
|
(DeprecationWarning, ".*pkg_resources.*"),
|
|
68
|
-
# This warning is triggered by sanic-cors 2.0.0.
|
|
58
|
+
# This warning is triggered by sanic-cors 2.0.0 and by langchain -> faiss.
|
|
69
59
|
# The warning can be removed after the packages are updated:
|
|
70
60
|
# sanic-cors: ^2.1.0
|
|
71
61
|
# packaging`: 23.2 (introduces breaking changes)
|
|
72
62
|
# pep440-version-utils (also requires update on packaging)
|
|
73
63
|
(
|
|
74
64
|
DeprecationWarning,
|
|
75
|
-
"distutils Version classes are deprecated. Use packaging.version instead
|
|
65
|
+
"distutils Version classes are deprecated. Use packaging.version instead",
|
|
76
66
|
),
|
|
77
67
|
# cf. https://github.com/tensorflow/tensorflow/issues/38168
|
|
78
68
|
(
|
|
@@ -81,11 +71,6 @@ EXPECTED_WARNINGS: List[Tuple[Type[Warning], str]] = [
|
|
|
81
71
|
"shape. This may consume a large amount of memory.",
|
|
82
72
|
),
|
|
83
73
|
(UserWarning, "Slot auto-fill has been removed in 3.0 .*"),
|
|
84
|
-
# This warning is caused by the flatbuffers package
|
|
85
|
-
# The import was fixed on Github, but the latest version
|
|
86
|
-
# is not available on PyPi, so we cannot pin the newer version.
|
|
87
|
-
# cf. https://github.com/google/flatbuffers/issues/6957
|
|
88
|
-
(DeprecationWarning, "the imp module is deprecated in favour of importlib.*"),
|
|
89
74
|
# Cannot fix this deprecation warning since we need to support two
|
|
90
75
|
# numpy versions as long as we keep python 37 around
|
|
91
76
|
(DeprecationWarning, "the `interpolation=` argument to quantile was renamed"),
|
|
@@ -102,10 +87,11 @@ EXPECTED_WARNINGS: List[Tuple[Type[Warning], str]] = [
|
|
|
102
87
|
DeprecationWarning,
|
|
103
88
|
"non-integer arguments to randrange\\(\\) have been deprecated since",
|
|
104
89
|
),
|
|
90
|
+
# Ignore Keras DeprecationWarning since it requires that we
|
|
91
|
+
# upgrade tensorflow-macos to 2.13.0 version.
|
|
105
92
|
(DeprecationWarning, "invalid escape sequence*"),
|
|
106
93
|
]
|
|
107
94
|
|
|
108
|
-
EXPECTED_WARNINGS.extend(EXPECTED_PILLOW_DEPRECATION_WARNINGS)
|
|
109
95
|
PYTHON_LOGGING_SCHEMA_DOCS = (
|
|
110
96
|
"https://docs.python.org/3/library/logging.config.html#dictionary-schema-details"
|
|
111
97
|
)
|
|
@@ -402,6 +388,19 @@ def update_faker_log_level(library_log_level: Text) -> None:
|
|
|
402
388
|
logging.getLogger("faker").propagate = False
|
|
403
389
|
|
|
404
390
|
|
|
391
|
+
def update_mlflow_log_level() -> None:
|
|
392
|
+
"""Set the log level of mlflow.
|
|
393
|
+
|
|
394
|
+
Uses the library specific log level or the general libraries log level.
|
|
395
|
+
"""
|
|
396
|
+
library_log_level = os.environ.get(
|
|
397
|
+
ENV_LOG_LEVEL_LIBRARIES, DEFAULT_LOG_LEVEL_LIBRARIES
|
|
398
|
+
)
|
|
399
|
+
log_level = os.environ.get(ENV_LOG_LEVEL_MLFLOW, library_log_level)
|
|
400
|
+
logging.getLogger("mlflow").setLevel(log_level)
|
|
401
|
+
logging.getLogger("mlflow").propagate = False
|
|
402
|
+
|
|
403
|
+
|
|
405
404
|
def sort_list_of_dicts_by_first_key(dicts: List[Dict]) -> List[Dict]:
|
|
406
405
|
"""Sorts a list of dictionaries by their first key."""
|
|
407
406
|
return sorted(dicts, key=lambda d: next(iter(d.keys())))
|
rasa/utils/endpoints.py
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
import os
|
|
1
2
|
import ssl
|
|
3
|
+
from types import ModuleType
|
|
4
|
+
from typing import Any, Optional, Text, Dict, Union
|
|
2
5
|
|
|
3
6
|
import aiohttp
|
|
4
|
-
import
|
|
7
|
+
import structlog
|
|
5
8
|
from aiohttp.client_exceptions import ContentTypeError
|
|
6
9
|
from sanic.request import Request
|
|
7
|
-
from typing import Any, Optional, Text, Dict
|
|
8
10
|
|
|
9
|
-
from rasa.shared.exceptions import FileNotFoundException
|
|
10
|
-
import structlog
|
|
11
11
|
from rasa.core.constants import DEFAULT_REQUEST_TIMEOUT
|
|
12
|
+
from rasa.shared.exceptions import FileNotFoundException
|
|
12
13
|
from rasa.shared.utils.yaml import read_config_file
|
|
13
14
|
|
|
14
15
|
structlogger = structlog.get_logger()
|
|
@@ -87,6 +88,7 @@ class EndpointConfig:
|
|
|
87
88
|
token: Optional[Text] = None,
|
|
88
89
|
token_name: Text = "token",
|
|
89
90
|
cafile: Optional[Text] = None,
|
|
91
|
+
actions_module: Optional[Union[Text, ModuleType]] = None,
|
|
90
92
|
**kwargs: Any,
|
|
91
93
|
) -> None:
|
|
92
94
|
"""Creates an `EndpointConfig` instance."""
|
|
@@ -98,6 +100,7 @@ class EndpointConfig:
|
|
|
98
100
|
self.token_name = token_name
|
|
99
101
|
self.type = kwargs.pop("store_type", kwargs.pop("type", None))
|
|
100
102
|
self.cafile = cafile
|
|
103
|
+
self.actions_module = actions_module
|
|
101
104
|
self.kwargs = kwargs
|
|
102
105
|
|
|
103
106
|
def session(self) -> aiohttp.ClientSession:
|
rasa/utils/io.py
CHANGED
|
@@ -2,6 +2,7 @@ import asyncio
|
|
|
2
2
|
import filecmp
|
|
3
3
|
import logging
|
|
4
4
|
import os
|
|
5
|
+
import pickle
|
|
5
6
|
import tempfile
|
|
6
7
|
import warnings
|
|
7
8
|
import re
|
|
@@ -97,6 +98,29 @@ def enable_async_loop_debugging(
|
|
|
97
98
|
return event_loop
|
|
98
99
|
|
|
99
100
|
|
|
101
|
+
def pickle_dump(filename: Union[Text, Path], obj: Any) -> None:
|
|
102
|
+
"""Saves object to file.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
filename: the filename to save the object to
|
|
106
|
+
obj: the object to store
|
|
107
|
+
"""
|
|
108
|
+
with open(filename, "wb") as f:
|
|
109
|
+
pickle.dump(obj, f)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def pickle_load(filename: Union[Text, Path]) -> Any:
|
|
113
|
+
"""Loads an object from a file.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
filename: the filename to load the object from
|
|
117
|
+
|
|
118
|
+
Returns: the loaded object
|
|
119
|
+
"""
|
|
120
|
+
with open(filename, "rb") as f:
|
|
121
|
+
return pickle.load(f)
|
|
122
|
+
|
|
123
|
+
|
|
100
124
|
def create_temporary_file(data: Any, suffix: Text = "", mode: Text = "w+") -> Text:
|
|
101
125
|
"""Creates a tempfile.NamedTemporaryFile object for data."""
|
|
102
126
|
encoding = None if "b" in mode else rasa.shared.utils.io.DEFAULT_ENCODING
|
|
@@ -167,6 +191,48 @@ def create_validator(
|
|
|
167
191
|
return FunctionValidator
|
|
168
192
|
|
|
169
193
|
|
|
194
|
+
def json_unpickle(
|
|
195
|
+
file_name: Union[Text, Path], encode_non_string_keys: bool = False
|
|
196
|
+
) -> Any:
|
|
197
|
+
"""Unpickle an object from file using json.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
file_name: the file to load the object from
|
|
201
|
+
encode_non_string_keys: If set to `True` then jsonpickle will encode non-string
|
|
202
|
+
dictionary keys instead of coercing them into strings via `repr()`.
|
|
203
|
+
|
|
204
|
+
Returns: the object
|
|
205
|
+
"""
|
|
206
|
+
import jsonpickle.ext.numpy as jsonpickle_numpy
|
|
207
|
+
import jsonpickle
|
|
208
|
+
|
|
209
|
+
jsonpickle_numpy.register_handlers()
|
|
210
|
+
|
|
211
|
+
file_content = rasa.shared.utils.io.read_file(file_name)
|
|
212
|
+
return jsonpickle.loads(file_content, keys=encode_non_string_keys)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def json_pickle(
|
|
216
|
+
file_name: Union[Text, Path], obj: Any, encode_non_string_keys: bool = False
|
|
217
|
+
) -> None:
|
|
218
|
+
"""Pickle an object to a file using json.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
file_name: the file to store the object to
|
|
222
|
+
obj: the object to store
|
|
223
|
+
encode_non_string_keys: If set to `True` then jsonpickle will encode non-string
|
|
224
|
+
dictionary keys instead of coercing them into strings via `repr()`.
|
|
225
|
+
"""
|
|
226
|
+
import jsonpickle.ext.numpy as jsonpickle_numpy
|
|
227
|
+
import jsonpickle
|
|
228
|
+
|
|
229
|
+
jsonpickle_numpy.register_handlers()
|
|
230
|
+
|
|
231
|
+
rasa.shared.utils.io.write_text_file(
|
|
232
|
+
jsonpickle.dumps(obj, keys=encode_non_string_keys), file_name
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
|
|
170
236
|
def get_emoji_regex() -> Pattern:
|
|
171
237
|
"""Returns regex to identify emojis."""
|
|
172
238
|
return re.compile(
|
rasa/utils/json_utils.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
from typing import Any, Text
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DecimalEncoder(json.JSONEncoder):
|
|
7
|
+
"""`json.JSONEncoder` that dumps `Decimal`s as `float`s."""
|
|
8
|
+
|
|
9
|
+
def default(self, obj: Any) -> Any:
|
|
10
|
+
"""Get serializable object for `o`.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
obj: Object to serialize.
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
`obj` converted to `float` if `o` is a `Decimals`, else the base class
|
|
17
|
+
`default()` method.
|
|
18
|
+
"""
|
|
19
|
+
if isinstance(obj, Decimal):
|
|
20
|
+
return float(obj)
|
|
21
|
+
return super().default(obj)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SetEncoder(json.JSONEncoder):
|
|
25
|
+
"""`json.JSONEncoder` that dumps `set`s as `list`s."""
|
|
26
|
+
|
|
27
|
+
def default(self, obj: Any) -> Any:
|
|
28
|
+
if isinstance(obj, set):
|
|
29
|
+
return list(obj)
|
|
30
|
+
return super().default(obj)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def replace_floats_with_decimals(obj: Any, round_digits: int = 9) -> Any:
|
|
34
|
+
"""Convert all instances in `obj` of `float` to `Decimal`.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
obj: Input object.
|
|
38
|
+
round_digits: Rounding precision of `Decimal` values.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Input `obj` with all `float` types replaced by `Decimal`s rounded to
|
|
42
|
+
`round_digits` decimal places.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def _float_to_rounded_decimal(s: Text) -> Decimal:
|
|
46
|
+
return Decimal(s).quantize(Decimal(10) ** -round_digits)
|
|
47
|
+
|
|
48
|
+
return json.loads(json.dumps(obj), parse_float=_float_to_rounded_decimal)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def replace_decimals_with_floats(obj: Any) -> Any:
|
|
52
|
+
"""Convert all instances in `obj` of `Decimal` to `float`.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
obj: A `List` or `Dict` object.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Input `obj` with all `Decimal` types replaced by `float`s.
|
|
59
|
+
"""
|
|
60
|
+
return json.loads(json.dumps(obj, cls=DecimalEncoder))
|
rasa/utils/licensing.py
CHANGED
|
@@ -293,7 +293,9 @@ def validate_license_from_env(product_area: Text = PRODUCT_AREA) -> None:
|
|
|
293
293
|
"Your license is about to expire. "
|
|
294
294
|
"Please contact Rasa for a renewal."
|
|
295
295
|
),
|
|
296
|
-
expiration_date=datetime.
|
|
296
|
+
expiration_date=datetime.fromtimestamp(
|
|
297
|
+
license.exp, timezone.utc
|
|
298
|
+
).isoformat(),
|
|
297
299
|
)
|
|
298
300
|
except LicenseNotFoundException:
|
|
299
301
|
structlogger.error("license.not_found.error")
|
|
@@ -311,6 +313,12 @@ def validate_license_from_env(product_area: Text = PRODUCT_AREA) -> None:
|
|
|
311
313
|
)
|
|
312
314
|
|
|
313
315
|
|
|
316
|
+
def get_license_expiration_date() -> Text:
|
|
317
|
+
"""Return the expiration date of the license."""
|
|
318
|
+
license_exp = property_of_active_license(lambda active_license: active_license.exp)
|
|
319
|
+
return datetime.fromtimestamp(license_exp, timezone.utc).isoformat()
|
|
320
|
+
|
|
321
|
+
|
|
314
322
|
def is_valid_license_scope(product_area: Text, license_scope: Text) -> bool:
|
|
315
323
|
"""Verifies that the license scope matches the rasa-plus product area."""
|
|
316
324
|
required_scopes = derive_scope_hierarchy(product_area)
|
rasa/utils/ml_utils.py
CHANGED
|
@@ -4,7 +4,7 @@ from typing import Any, Dict, List, Optional, Text
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
import structlog
|
|
6
6
|
from langchain.schema.embeddings import Embeddings
|
|
7
|
-
from
|
|
7
|
+
from langchain_community.vectorstores import FAISS
|
|
8
8
|
from rasa.shared.constants import REQUIRED_SLOTS_KEY
|
|
9
9
|
from rasa.shared.core.domain import KEY_RESPONSES_TEXT, Domain
|
|
10
10
|
from rasa.shared.utils.llm import AI
|
|
@@ -34,7 +34,9 @@ def load_faiss_vector_store(path: Path, embedder: Embeddings) -> Optional[FAISS]
|
|
|
34
34
|
The loaded vector store or None if the path does not exist.
|
|
35
35
|
"""
|
|
36
36
|
if path.exists():
|
|
37
|
-
return FAISS.load_local(
|
|
37
|
+
return FAISS.load_local(
|
|
38
|
+
str(path), embedder, allow_dangerous_deserialization=True
|
|
39
|
+
)
|
|
38
40
|
else:
|
|
39
41
|
return None
|
|
40
42
|
|
|
@@ -20,8 +20,6 @@ import numpy as np
|
|
|
20
20
|
import scipy.sparse
|
|
21
21
|
from sklearn.model_selection import train_test_split
|
|
22
22
|
|
|
23
|
-
from rasa.utils.tensorflow.feature_array import FeatureArray
|
|
24
|
-
|
|
25
23
|
logger = logging.getLogger(__name__)
|
|
26
24
|
|
|
27
25
|
|
|
@@ -39,6 +37,199 @@ def ragged_array_to_ndarray(ragged_array: Iterable[np.ndarray]) -> np.ndarray:
|
|
|
39
37
|
return np.array(ragged_array, dtype=object)
|
|
40
38
|
|
|
41
39
|
|
|
40
|
+
class FeatureArray(np.ndarray):
|
|
41
|
+
"""Stores any kind of features ready to be used by a RasaModel.
|
|
42
|
+
|
|
43
|
+
Next to the input numpy array of features, it also received the number of
|
|
44
|
+
dimensions of the features.
|
|
45
|
+
As our features can have 1 to 4 dimensions we might have different number of numpy
|
|
46
|
+
arrays stacked. The number of dimensions helps us to figure out how to handle this
|
|
47
|
+
particular feature array. Also, it is automatically determined whether the feature
|
|
48
|
+
array is sparse or not and the number of units is determined as well.
|
|
49
|
+
|
|
50
|
+
Subclassing np.array: https://numpy.org/doc/stable/user/basics.subclassing.html
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __new__(
|
|
54
|
+
cls, input_array: np.ndarray, number_of_dimensions: int
|
|
55
|
+
) -> "FeatureArray":
|
|
56
|
+
"""Create and return a new object. See help(type) for accurate signature."""
|
|
57
|
+
FeatureArray._validate_number_of_dimensions(number_of_dimensions, input_array)
|
|
58
|
+
|
|
59
|
+
feature_array = np.asarray(input_array).view(cls)
|
|
60
|
+
|
|
61
|
+
if number_of_dimensions <= 2:
|
|
62
|
+
feature_array.units = input_array.shape[-1]
|
|
63
|
+
feature_array.is_sparse = isinstance(input_array[0], scipy.sparse.spmatrix)
|
|
64
|
+
elif number_of_dimensions == 3:
|
|
65
|
+
feature_array.units = input_array[0].shape[-1]
|
|
66
|
+
feature_array.is_sparse = isinstance(input_array[0], scipy.sparse.spmatrix)
|
|
67
|
+
elif number_of_dimensions == 4:
|
|
68
|
+
feature_array.units = input_array[0][0].shape[-1]
|
|
69
|
+
feature_array.is_sparse = isinstance(
|
|
70
|
+
input_array[0][0], scipy.sparse.spmatrix
|
|
71
|
+
)
|
|
72
|
+
else:
|
|
73
|
+
raise ValueError(
|
|
74
|
+
f"Number of dimensions '{number_of_dimensions}' currently not "
|
|
75
|
+
f"supported."
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
feature_array.number_of_dimensions = number_of_dimensions
|
|
79
|
+
|
|
80
|
+
return feature_array
|
|
81
|
+
|
|
82
|
+
def __init__(
|
|
83
|
+
self, input_array: Any, number_of_dimensions: int, **kwargs: Any
|
|
84
|
+
) -> None:
|
|
85
|
+
"""Initialize. FeatureArray.
|
|
86
|
+
|
|
87
|
+
Needed in order to avoid 'Invalid keyword argument number_of_dimensions
|
|
88
|
+
to function FeatureArray.__init__ '
|
|
89
|
+
Args:
|
|
90
|
+
input_array: the array that contains features
|
|
91
|
+
number_of_dimensions: number of dimensions in input_array
|
|
92
|
+
"""
|
|
93
|
+
super().__init__(**kwargs)
|
|
94
|
+
self.number_of_dimensions = number_of_dimensions
|
|
95
|
+
|
|
96
|
+
def __array_finalize__(self, obj: Optional[np.ndarray]) -> None:
|
|
97
|
+
"""This method is called when the system allocates a new array from obj.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
obj: A subclass (subtype) of ndarray.
|
|
101
|
+
"""
|
|
102
|
+
if obj is None:
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
self.units = getattr(obj, "units", None)
|
|
106
|
+
self.number_of_dimensions = getattr(obj, "number_of_dimensions", None) # type: ignore[assignment]
|
|
107
|
+
self.is_sparse = getattr(obj, "is_sparse", None)
|
|
108
|
+
|
|
109
|
+
default_attributes = {
|
|
110
|
+
"units": self.units,
|
|
111
|
+
"number_of_dimensions": self.number_of_dimensions,
|
|
112
|
+
"is_spare": self.is_sparse,
|
|
113
|
+
}
|
|
114
|
+
self.__dict__.update(default_attributes)
|
|
115
|
+
|
|
116
|
+
# pytype: disable=attribute-error
|
|
117
|
+
def __array_ufunc__(
|
|
118
|
+
self, ufunc: Any, method: Text, *inputs: Any, **kwargs: Any
|
|
119
|
+
) -> Any:
|
|
120
|
+
"""Overwrite this method as we are subclassing numpy array.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
ufunc: The ufunc object that was called.
|
|
124
|
+
method: A string indicating which Ufunc method was called
|
|
125
|
+
(one of "__call__", "reduce", "reduceat", "accumulate", "outer",
|
|
126
|
+
"inner").
|
|
127
|
+
*inputs: A tuple of the input arguments to the ufunc.
|
|
128
|
+
**kwargs: Any additional arguments
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
The result of the operation.
|
|
132
|
+
"""
|
|
133
|
+
f = {
|
|
134
|
+
"reduce": ufunc.reduce,
|
|
135
|
+
"accumulate": ufunc.accumulate,
|
|
136
|
+
"reduceat": ufunc.reduceat,
|
|
137
|
+
"outer": ufunc.outer,
|
|
138
|
+
"at": ufunc.at,
|
|
139
|
+
"__call__": ufunc,
|
|
140
|
+
}
|
|
141
|
+
# convert the inputs to np.ndarray to prevent recursion, call the function,
|
|
142
|
+
# then cast it back as FeatureArray
|
|
143
|
+
output = FeatureArray(
|
|
144
|
+
f[method](*(i.view(np.ndarray) for i in inputs), **kwargs),
|
|
145
|
+
number_of_dimensions=kwargs["number_of_dimensions"],
|
|
146
|
+
)
|
|
147
|
+
output.__dict__ = self.__dict__ # carry forward attributes
|
|
148
|
+
return output
|
|
149
|
+
|
|
150
|
+
def __reduce__(self) -> Tuple[Any, Any, Any]:
|
|
151
|
+
"""Needed in order to pickle this object.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
A tuple.
|
|
155
|
+
"""
|
|
156
|
+
pickled_state = super(FeatureArray, self).__reduce__()
|
|
157
|
+
if isinstance(pickled_state, str):
|
|
158
|
+
raise TypeError("np array __reduce__ returned string instead of tuple.")
|
|
159
|
+
new_state = pickled_state[2] + (
|
|
160
|
+
self.number_of_dimensions,
|
|
161
|
+
self.is_sparse,
|
|
162
|
+
self.units,
|
|
163
|
+
)
|
|
164
|
+
return pickled_state[0], pickled_state[1], new_state
|
|
165
|
+
|
|
166
|
+
def __setstate__(self, state: Any, **kwargs: Any) -> None:
|
|
167
|
+
"""Sets the state.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
state: The state argument must be a sequence that contains the following
|
|
171
|
+
elements version, shape, dtype, isFortan, rawdata.
|
|
172
|
+
**kwargs: Any additional parameter
|
|
173
|
+
"""
|
|
174
|
+
# Needed in order to load the object
|
|
175
|
+
self.number_of_dimensions = state[-3]
|
|
176
|
+
self.is_sparse = state[-2]
|
|
177
|
+
self.units = state[-1]
|
|
178
|
+
super(FeatureArray, self).__setstate__(state[0:-3], **kwargs)
|
|
179
|
+
|
|
180
|
+
# pytype: enable=attribute-error
|
|
181
|
+
|
|
182
|
+
@staticmethod
|
|
183
|
+
def _validate_number_of_dimensions(
|
|
184
|
+
number_of_dimensions: int, input_array: np.ndarray
|
|
185
|
+
) -> None:
|
|
186
|
+
"""Validates if the the input array has given number of dimensions.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
number_of_dimensions: number of dimensions
|
|
190
|
+
input_array: input array
|
|
191
|
+
|
|
192
|
+
Raises: ValueError in case the dimensions do not match
|
|
193
|
+
"""
|
|
194
|
+
_sub_array = input_array
|
|
195
|
+
dim = 0
|
|
196
|
+
# Go number_of_dimensions into the given input_array
|
|
197
|
+
for i in range(1, number_of_dimensions + 1):
|
|
198
|
+
_sub_array = _sub_array[0]
|
|
199
|
+
if isinstance(_sub_array, scipy.sparse.spmatrix):
|
|
200
|
+
dim = i
|
|
201
|
+
break
|
|
202
|
+
if isinstance(_sub_array, np.ndarray) and _sub_array.shape[0] == 0:
|
|
203
|
+
# sequence dimension is 0, we are dealing with "fake" features
|
|
204
|
+
dim = i
|
|
205
|
+
break
|
|
206
|
+
|
|
207
|
+
# If the resulting sub_array is sparse, the remaining number of dimensions
|
|
208
|
+
# should be at least 2
|
|
209
|
+
if isinstance(_sub_array, scipy.sparse.spmatrix):
|
|
210
|
+
if dim > 2:
|
|
211
|
+
raise ValueError(
|
|
212
|
+
f"Given number of dimensions '{number_of_dimensions}' does not "
|
|
213
|
+
f"match dimensions of given input array: {input_array}."
|
|
214
|
+
)
|
|
215
|
+
elif isinstance(_sub_array, np.ndarray) and _sub_array.shape[0] == 0:
|
|
216
|
+
# sequence dimension is 0, we are dealing with "fake" features,
|
|
217
|
+
# but they should be of dim 2
|
|
218
|
+
if dim > 2:
|
|
219
|
+
raise ValueError(
|
|
220
|
+
f"Given number of dimensions '{number_of_dimensions}' does not "
|
|
221
|
+
f"match dimensions of given input array: {input_array}."
|
|
222
|
+
)
|
|
223
|
+
# If the resulting sub_array is dense, the sub_array should be a single number
|
|
224
|
+
elif not np.issubdtype(type(_sub_array), np.integer) and not isinstance(
|
|
225
|
+
_sub_array, (np.float32, np.float64)
|
|
226
|
+
):
|
|
227
|
+
raise ValueError(
|
|
228
|
+
f"Given number of dimensions '{number_of_dimensions}' does not match "
|
|
229
|
+
f"dimensions of given input array: {input_array}."
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
|
|
42
233
|
class FeatureSignature(NamedTuple):
|
|
43
234
|
"""Signature of feature arrays.
|
|
44
235
|
|