rasa-pro 3.9.18__py3-none-any.whl → 3.10.16__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.

Files changed (183) hide show
  1. README.md +0 -374
  2. rasa/__init__.py +1 -2
  3. rasa/__main__.py +5 -0
  4. rasa/anonymization/anonymization_rule_executor.py +2 -2
  5. rasa/api.py +27 -23
  6. rasa/cli/arguments/data.py +27 -2
  7. rasa/cli/arguments/default_arguments.py +25 -3
  8. rasa/cli/arguments/run.py +9 -9
  9. rasa/cli/arguments/train.py +11 -3
  10. rasa/cli/data.py +70 -8
  11. rasa/cli/e2e_test.py +104 -431
  12. rasa/cli/evaluate.py +1 -1
  13. rasa/cli/interactive.py +1 -0
  14. rasa/cli/llm_fine_tuning.py +398 -0
  15. rasa/cli/project_templates/calm/endpoints.yml +1 -1
  16. rasa/cli/project_templates/tutorial/endpoints.yml +1 -1
  17. rasa/cli/run.py +15 -14
  18. rasa/cli/scaffold.py +10 -8
  19. rasa/cli/studio/studio.py +35 -5
  20. rasa/cli/train.py +56 -8
  21. rasa/cli/utils.py +22 -5
  22. rasa/cli/x.py +1 -1
  23. rasa/constants.py +7 -1
  24. rasa/core/actions/action.py +98 -49
  25. rasa/core/actions/action_run_slot_rejections.py +4 -1
  26. rasa/core/actions/custom_action_executor.py +9 -6
  27. rasa/core/actions/direct_custom_actions_executor.py +80 -0
  28. rasa/core/actions/e2e_stub_custom_action_executor.py +68 -0
  29. rasa/core/actions/grpc_custom_action_executor.py +2 -2
  30. rasa/core/actions/http_custom_action_executor.py +6 -5
  31. rasa/core/agent.py +21 -17
  32. rasa/core/channels/__init__.py +2 -0
  33. rasa/core/channels/audiocodes.py +1 -16
  34. rasa/core/channels/voice_aware/__init__.py +0 -0
  35. rasa/core/channels/voice_aware/jambonz.py +103 -0
  36. rasa/core/channels/voice_aware/jambonz_protocol.py +344 -0
  37. rasa/core/channels/voice_aware/utils.py +20 -0
  38. rasa/core/channels/voice_native/__init__.py +0 -0
  39. rasa/core/constants.py +6 -1
  40. rasa/core/information_retrieval/faiss.py +7 -4
  41. rasa/core/information_retrieval/information_retrieval.py +8 -0
  42. rasa/core/information_retrieval/milvus.py +9 -2
  43. rasa/core/information_retrieval/qdrant.py +1 -1
  44. rasa/core/nlg/contextual_response_rephraser.py +32 -10
  45. rasa/core/nlg/summarize.py +4 -3
  46. rasa/core/policies/enterprise_search_policy.py +113 -45
  47. rasa/core/policies/flows/flow_executor.py +122 -76
  48. rasa/core/policies/intentless_policy.py +83 -29
  49. rasa/core/processor.py +72 -54
  50. rasa/core/run.py +5 -4
  51. rasa/core/tracker_store.py +8 -4
  52. rasa/core/training/interactive.py +1 -1
  53. rasa/core/utils.py +56 -57
  54. rasa/dialogue_understanding/coexistence/llm_based_router.py +53 -13
  55. rasa/dialogue_understanding/commands/__init__.py +6 -0
  56. rasa/dialogue_understanding/commands/restart_command.py +58 -0
  57. rasa/dialogue_understanding/commands/session_start_command.py +59 -0
  58. rasa/dialogue_understanding/commands/utils.py +40 -0
  59. rasa/dialogue_understanding/generator/constants.py +10 -3
  60. rasa/dialogue_understanding/generator/flow_retrieval.py +21 -5
  61. rasa/dialogue_understanding/generator/llm_based_command_generator.py +13 -3
  62. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +134 -90
  63. rasa/dialogue_understanding/generator/nlu_command_adapter.py +47 -7
  64. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +127 -41
  65. rasa/dialogue_understanding/patterns/restart.py +37 -0
  66. rasa/dialogue_understanding/patterns/session_start.py +37 -0
  67. rasa/dialogue_understanding/processor/command_processor.py +16 -3
  68. rasa/dialogue_understanding/processor/command_processor_component.py +6 -2
  69. rasa/e2e_test/aggregate_test_stats_calculator.py +134 -0
  70. rasa/e2e_test/assertions.py +1223 -0
  71. rasa/e2e_test/assertions_schema.yml +106 -0
  72. rasa/e2e_test/constants.py +20 -0
  73. rasa/e2e_test/e2e_config.py +220 -0
  74. rasa/e2e_test/e2e_config_schema.yml +26 -0
  75. rasa/e2e_test/e2e_test_case.py +131 -8
  76. rasa/e2e_test/e2e_test_converter.py +363 -0
  77. rasa/e2e_test/e2e_test_converter_prompt.jinja2 +70 -0
  78. rasa/e2e_test/e2e_test_coverage_report.py +364 -0
  79. rasa/e2e_test/e2e_test_result.py +26 -6
  80. rasa/e2e_test/e2e_test_runner.py +493 -71
  81. rasa/e2e_test/e2e_test_schema.yml +96 -0
  82. rasa/e2e_test/pykwalify_extensions.py +39 -0
  83. rasa/e2e_test/stub_custom_action.py +70 -0
  84. rasa/e2e_test/utils/__init__.py +0 -0
  85. rasa/e2e_test/utils/e2e_yaml_utils.py +55 -0
  86. rasa/e2e_test/utils/io.py +598 -0
  87. rasa/e2e_test/utils/validation.py +80 -0
  88. rasa/engine/graph.py +9 -3
  89. rasa/engine/recipes/default_components.py +0 -2
  90. rasa/engine/recipes/default_recipe.py +10 -2
  91. rasa/engine/storage/local_model_storage.py +40 -12
  92. rasa/engine/validation.py +78 -1
  93. rasa/env.py +9 -0
  94. rasa/graph_components/providers/story_graph_provider.py +59 -6
  95. rasa/llm_fine_tuning/__init__.py +0 -0
  96. rasa/llm_fine_tuning/annotation_module.py +241 -0
  97. rasa/llm_fine_tuning/conversations.py +144 -0
  98. rasa/llm_fine_tuning/llm_data_preparation_module.py +178 -0
  99. rasa/llm_fine_tuning/notebooks/unsloth_finetuning.ipynb +407 -0
  100. rasa/llm_fine_tuning/paraphrasing/__init__.py +0 -0
  101. rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +281 -0
  102. rasa/llm_fine_tuning/paraphrasing/default_rephrase_prompt_template.jina2 +44 -0
  103. rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +121 -0
  104. rasa/llm_fine_tuning/paraphrasing/rephrased_user_message.py +10 -0
  105. rasa/llm_fine_tuning/paraphrasing_module.py +128 -0
  106. rasa/llm_fine_tuning/storage.py +174 -0
  107. rasa/llm_fine_tuning/train_test_split_module.py +441 -0
  108. rasa/model_training.py +56 -16
  109. rasa/nlu/persistor.py +157 -36
  110. rasa/server.py +45 -10
  111. rasa/shared/constants.py +76 -16
  112. rasa/shared/core/domain.py +27 -19
  113. rasa/shared/core/events.py +28 -2
  114. rasa/shared/core/flows/flow.py +208 -13
  115. rasa/shared/core/flows/flow_path.py +84 -0
  116. rasa/shared/core/flows/flows_list.py +33 -11
  117. rasa/shared/core/flows/flows_yaml_schema.json +269 -193
  118. rasa/shared/core/flows/validation.py +112 -25
  119. rasa/shared/core/flows/yaml_flows_io.py +149 -10
  120. rasa/shared/core/trackers.py +6 -0
  121. rasa/shared/core/training_data/structures.py +20 -0
  122. rasa/shared/core/training_data/visualization.html +2 -2
  123. rasa/shared/exceptions.py +4 -0
  124. rasa/shared/importers/importer.py +64 -16
  125. rasa/shared/nlu/constants.py +2 -0
  126. rasa/shared/providers/_configs/__init__.py +0 -0
  127. rasa/shared/providers/_configs/azure_openai_client_config.py +183 -0
  128. rasa/shared/providers/_configs/client_config.py +57 -0
  129. rasa/shared/providers/_configs/default_litellm_client_config.py +130 -0
  130. rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +234 -0
  131. rasa/shared/providers/_configs/openai_client_config.py +175 -0
  132. rasa/shared/providers/_configs/self_hosted_llm_client_config.py +176 -0
  133. rasa/shared/providers/_configs/utils.py +101 -0
  134. rasa/shared/providers/_ssl_verification_utils.py +124 -0
  135. rasa/shared/providers/embedding/__init__.py +0 -0
  136. rasa/shared/providers/embedding/_base_litellm_embedding_client.py +259 -0
  137. rasa/shared/providers/embedding/_langchain_embedding_client_adapter.py +74 -0
  138. rasa/shared/providers/embedding/azure_openai_embedding_client.py +277 -0
  139. rasa/shared/providers/embedding/default_litellm_embedding_client.py +102 -0
  140. rasa/shared/providers/embedding/embedding_client.py +90 -0
  141. rasa/shared/providers/embedding/embedding_response.py +41 -0
  142. rasa/shared/providers/embedding/huggingface_local_embedding_client.py +191 -0
  143. rasa/shared/providers/embedding/openai_embedding_client.py +172 -0
  144. rasa/shared/providers/llm/__init__.py +0 -0
  145. rasa/shared/providers/llm/_base_litellm_client.py +251 -0
  146. rasa/shared/providers/llm/azure_openai_llm_client.py +338 -0
  147. rasa/shared/providers/llm/default_litellm_llm_client.py +84 -0
  148. rasa/shared/providers/llm/llm_client.py +76 -0
  149. rasa/shared/providers/llm/llm_response.py +50 -0
  150. rasa/shared/providers/llm/openai_llm_client.py +155 -0
  151. rasa/shared/providers/llm/self_hosted_llm_client.py +293 -0
  152. rasa/shared/providers/mappings.py +75 -0
  153. rasa/shared/utils/cli.py +30 -0
  154. rasa/shared/utils/io.py +65 -2
  155. rasa/shared/utils/llm.py +246 -200
  156. rasa/shared/utils/yaml.py +121 -15
  157. rasa/studio/auth.py +6 -4
  158. rasa/studio/config.py +13 -4
  159. rasa/studio/constants.py +1 -0
  160. rasa/studio/data_handler.py +10 -3
  161. rasa/studio/download.py +19 -13
  162. rasa/studio/train.py +2 -3
  163. rasa/studio/upload.py +19 -11
  164. rasa/telemetry.py +113 -58
  165. rasa/tracing/instrumentation/attribute_extractors.py +32 -17
  166. rasa/utils/common.py +18 -19
  167. rasa/utils/endpoints.py +7 -4
  168. rasa/utils/json_utils.py +60 -0
  169. rasa/utils/licensing.py +9 -1
  170. rasa/utils/ml_utils.py +4 -2
  171. rasa/validator.py +213 -3
  172. rasa/version.py +1 -1
  173. rasa_pro-3.10.16.dist-info/METADATA +196 -0
  174. {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/RECORD +179 -113
  175. rasa/nlu/classifiers/llm_intent_classifier.py +0 -519
  176. rasa/shared/providers/openai/clients.py +0 -43
  177. rasa/shared/providers/openai/session_handler.py +0 -110
  178. rasa_pro-3.9.18.dist-info/METADATA +0 -563
  179. /rasa/{shared/providers/openai → cli/project_templates/tutorial/actions}/__init__.py +0 -0
  180. /rasa/cli/project_templates/tutorial/{actions.py → actions/actions.py} +0 -0
  181. {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/NOTICE +0 -0
  182. {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/WHEEL +0 -0
  183. {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/entry_points.txt +0 -0
rasa/cli/train.py CHANGED
@@ -1,26 +1,34 @@
1
1
  import argparse
2
- import structlog
3
- import sys
4
2
  import asyncio
3
+ import os
4
+ import sys
5
5
  from pathlib import Path
6
6
  from typing import Dict, List, Optional, Text, Union
7
7
 
8
- from rasa.cli import SubParsersAction
9
- import rasa.cli.arguments.train as train_arguments
8
+ import structlog
9
+ from tarsafe import TarSafe
10
10
 
11
+ import rasa.cli.arguments.train as train_arguments
11
12
  import rasa.cli.utils
12
13
  import rasa.core.utils
13
- from rasa.shared.importers.importer import TrainingDataImporter
14
14
  import rasa.utils.common
15
+ from rasa.api import train as train_all
16
+ from rasa.cli import SubParsersAction
17
+ from rasa.core import ContextualResponseRephraser
15
18
  from rasa.core.nlg.generator import NaturalLanguageGenerator
16
19
  from rasa.core.train import do_compare_training
20
+ from rasa.engine.validation import validate_api_type_config_key_usage
21
+ from rasa.nlu.persistor import get_persistor
17
22
  from rasa.shared.constants import (
23
+ CONFIG_MANDATORY_KEYS,
18
24
  CONFIG_MANDATORY_KEYS_CORE,
19
25
  CONFIG_MANDATORY_KEYS_NLU,
20
- CONFIG_MANDATORY_KEYS,
21
26
  DEFAULT_DATA_PATH,
22
27
  DEFAULT_DOMAIN_PATHS,
28
+ LLM_CONFIG_KEY,
23
29
  )
30
+ from rasa.shared.exceptions import RasaException
31
+ from rasa.shared.importers.importer import TrainingDataImporter
24
32
 
25
33
  structlogger = structlog.getLogger(__name__)
26
34
 
@@ -36,7 +44,7 @@ def add_subparser(
36
44
  """
37
45
  train_parser = subparsers.add_parser(
38
46
  "train",
39
- help="Trains a Rasa model using your NLU data and stories.",
47
+ help="Trains a Rasa model using your CALM flows, NLU data and stories.",
40
48
  parents=parents,
41
49
  formatter_class=argparse.ArgumentDefaultsHelpFormatter,
42
50
  )
@@ -70,6 +78,12 @@ def add_subparser(
70
78
  def _check_nlg_endpoint_validity(endpoint: Union[Path, str]) -> None:
71
79
  try:
72
80
  endpoints = rasa.core.utils.read_endpoints_from_path(endpoint)
81
+ if endpoints.nlg is not None:
82
+ validate_api_type_config_key_usage(
83
+ endpoints.nlg.kwargs,
84
+ LLM_CONFIG_KEY,
85
+ ContextualResponseRephraser.__name__,
86
+ )
73
87
  NaturalLanguageGenerator.create(endpoints.nlg)
74
88
  except Exception as e:
75
89
  structlogger.error(
@@ -83,6 +97,37 @@ def _check_nlg_endpoint_validity(endpoint: Union[Path, str]) -> None:
83
97
  sys.exit(1)
84
98
 
85
99
 
100
+ def retrieve_and_unpack_bot_config_from_remote_storage(
101
+ args: argparse.Namespace,
102
+ ) -> None:
103
+ """Retrieve and unpack bot config from remote storage.
104
+
105
+ Bot config is retrieved from remote storage and unpacked
106
+ to the current working directory.
107
+ """
108
+ persistor = get_persistor(args.remote_storage)
109
+ if persistor is None:
110
+ raise RasaException(
111
+ f"Could not find a persistor for "
112
+ f"the storage type '{args.remote_storage}'."
113
+ )
114
+
115
+ current_working_directory = os.getcwd()
116
+
117
+ persistor.retrieve(args.remote_bot_config_path, current_working_directory)
118
+
119
+ remote_bot_config_tar_file_name = os.path.basename(args.remote_bot_config_path)
120
+
121
+ with TarSafe.open(remote_bot_config_tar_file_name, "r:gz") as tar:
122
+ tar.extractall(path=current_working_directory)
123
+
124
+ structlogger.debug(
125
+ "rasa.train.retrieve_and_unpack_bot_config.remove_downloaded_archive",
126
+ training_data_path=args.remote_bot_config_path,
127
+ )
128
+ os.remove(Path(current_working_directory).joinpath(remote_bot_config_tar_file_name))
129
+
130
+
86
131
  def run_training(args: argparse.Namespace, can_exit: bool = False) -> Optional[Text]:
87
132
  """Trains a model.
88
133
 
@@ -94,7 +139,9 @@ def run_training(args: argparse.Namespace, can_exit: bool = False) -> Optional[T
94
139
  Returns:
95
140
  Path to a trained model or `None` if training was not successful.
96
141
  """
97
- from rasa import train as train_all
142
+ # retrieve and unpack bot_config from remote storage
143
+ if hasattr(args, "remote_bot_config_path") and args.remote_bot_config_path:
144
+ retrieve_and_unpack_bot_config_from_remote_storage(args)
98
145
 
99
146
  domain = rasa.cli.utils.get_validated_path(
100
147
  args.domain, "domain", DEFAULT_DOMAIN_PATHS, none_is_valid=True
@@ -137,6 +184,7 @@ def run_training(args: argparse.Namespace, can_exit: bool = False) -> Optional[T
137
184
  nlu_additional_arguments=extract_nlu_additional_arguments(args),
138
185
  model_to_finetune=_model_for_finetuning(args),
139
186
  finetuning_epoch_fraction=args.epoch_fraction,
187
+ remote_storage=args.remote_storage,
140
188
  )
141
189
  if training_result.code != 0 and can_exit:
142
190
  sys.exit(training_result.code)
rasa/cli/utils.py CHANGED
@@ -1,30 +1,32 @@
1
- import json
2
1
  import argparse
3
- import structlog
4
2
  import importlib
3
+ import json
5
4
  import os
6
5
  import sys
7
6
  import time
8
7
  from pathlib import Path
9
8
  from types import FrameType
10
- from typing import Any, Dict, List, Optional, TYPE_CHECKING, Text, Union, overload
9
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Text, Union, overload
10
+
11
11
  import randomname
12
+ import structlog
12
13
 
13
14
  import rasa.shared.utils.cli
14
15
  import rasa.shared.utils.io
15
- from rasa.shared.importers.importer import TrainingDataImporter
16
+ from rasa import telemetry
16
17
  from rasa.shared.constants import (
17
18
  ASSISTANT_ID_DEFAULT_VALUE,
18
19
  ASSISTANT_ID_KEY,
19
20
  DEFAULT_CONFIG_PATH,
20
21
  )
21
- from rasa import telemetry
22
+ from rasa.shared.importers.importer import TrainingDataImporter
22
23
  from rasa.shared.utils.yaml import read_config_file
23
24
  from rasa.utils.io import write_yaml
24
25
 
25
26
  if TYPE_CHECKING:
26
27
  from questionary import Question
27
28
  from typing_extensions import Literal
29
+
28
30
  from rasa.validator import Validator
29
31
 
30
32
  structlogger = structlog.get_logger()
@@ -462,3 +464,18 @@ def warn_if_rasa_plus_package_installed() -> None:
462
464
  def check_if_studio_command() -> bool:
463
465
  """Checks if the command is a Rasa Studio command."""
464
466
  return len(sys.argv) >= 2 and sys.argv[1] == "studio"
467
+
468
+
469
+ def get_e2e_results_file_name(
470
+ results_output_path: Path,
471
+ result_type: str,
472
+ ) -> str:
473
+ """Returns the name of the e2e results file."""
474
+ if results_output_path.is_dir():
475
+ file_name = str(results_output_path) + f"/e2e_results_{result_type}.yml"
476
+ else:
477
+ parent = results_output_path.parent
478
+ stem = results_output_path.stem
479
+ file_name = str(parent) + f"/{stem}_{result_type}.yml"
480
+
481
+ return file_name
rasa/cli/x.py CHANGED
@@ -179,7 +179,7 @@ def run_in_enterprise_connection_mode(args: argparse.Namespace) -> None:
179
179
  print_success("Starting a Rasa server in Rasa Enterprise connection mode... 🚀")
180
180
 
181
181
  credentials_path, endpoints_path = _get_credentials_and_endpoints_paths(args)
182
- endpoints = AvailableEndpoints.read_endpoints(endpoints_path)
182
+ endpoints = AvailableEndpoints.get_instance(endpoints_path)
183
183
 
184
184
  _rasa_service(args, endpoints, None, credentials_path)
185
185
 
rasa/constants.py CHANGED
@@ -18,7 +18,7 @@ CONFIG_TELEMETRY_ID = "rasa_user_id"
18
18
  CONFIG_TELEMETRY_ENABLED = "enabled"
19
19
  CONFIG_TELEMETRY_DATE = "date"
20
20
 
21
- MINIMUM_COMPATIBLE_VERSION = "3.9.16"
21
+ MINIMUM_COMPATIBLE_VERSION = "3.10.12"
22
22
 
23
23
  GLOBAL_USER_CONFIG_PATH = os.path.expanduser("~/.config/rasa/global.yml")
24
24
 
@@ -27,6 +27,7 @@ ENV_LOG_LEVEL_LIBRARIES = "LOG_LEVEL_LIBRARIES"
27
27
  ENV_LOG_LEVEL_MATPLOTLIB = "LOG_LEVEL_MATPLOTLIB"
28
28
  ENV_LOG_LEVEL_RABBITMQ = "LOG_LEVEL_RABBITMQ"
29
29
  ENV_LOG_LEVEL_KAFKA = "LOG_LEVEL_KAFKA"
30
+ ENV_LOG_LEVEL_MLFLOW = "LOG_LEVEL_MLFLOW"
30
31
 
31
32
  DEFAULT_SANIC_WORKERS = 1
32
33
  ENV_SANIC_WORKERS = "SANIC_WORKERS"
@@ -35,3 +36,8 @@ ENV_SANIC_BACKLOG = "SANIC_BACKLOG"
35
36
  ENV_GPU_CONFIG = "TF_GPU_MEMORY_ALLOC"
36
37
  ENV_CPU_INTER_OP_CONFIG = "TF_INTER_OP_PARALLELISM_THREADS"
37
38
  ENV_CPU_INTRA_OP_CONFIG = "TF_INTRA_OP_PARALLELISM_THREADS"
39
+
40
+ MODEL_ARCHIVE_EXTENSION = "tar.gz"
41
+
42
+ HTTP_STATUS_FORBIDDEN = 403
43
+ HTTP_STATUS_NOT_FOUND = 404
@@ -1,14 +1,15 @@
1
1
  import copy
2
2
  import logging
3
+ from functools import lru_cache
3
4
  from typing import (
5
+ TYPE_CHECKING,
6
+ Any,
7
+ Dict,
4
8
  List,
5
- Text,
6
9
  Optional,
7
- Dict,
8
- Any,
9
- TYPE_CHECKING,
10
- Tuple,
11
10
  Set,
11
+ Text,
12
+ Tuple,
12
13
  cast,
13
14
  )
14
15
 
@@ -16,59 +17,68 @@ import rasa.core
16
17
  import rasa.shared.utils.io
17
18
  from rasa.core.actions.custom_action_executor import (
18
19
  CustomActionExecutor,
19
- RetryCustomActionExecutor,
20
20
  NoEndpointCustomActionExecutor,
21
+ RetryCustomActionExecutor,
22
+ )
23
+ from rasa.core.actions.direct_custom_actions_executor import DirectCustomActionExecutor
24
+ from rasa.core.actions.e2e_stub_custom_action_executor import (
25
+ E2EStubCustomActionExecutor,
21
26
  )
22
27
  from rasa.core.actions.grpc_custom_action_executor import GRPCCustomActionExecutor
23
28
  from rasa.core.actions.http_custom_action_executor import HTTPCustomActionExecutor
29
+ from rasa.core.constants import (
30
+ UTTER_SOURCE_METADATA_KEY,
31
+ )
24
32
  from rasa.core.policies.policy import PolicyPrediction
33
+ from rasa.core.utils import add_bot_utterance_metadata
34
+ from rasa.e2e_test.constants import KEY_STUB_CUSTOM_ACTIONS
25
35
  from rasa.nlu.constants import (
26
36
  RESPONSE_SELECTOR_DEFAULT_INTENT,
27
- RESPONSE_SELECTOR_PROPERTY_NAME,
28
37
  RESPONSE_SELECTOR_PREDICTION_KEY,
38
+ RESPONSE_SELECTOR_PROPERTY_NAME,
29
39
  RESPONSE_SELECTOR_UTTER_ACTION_KEY,
30
40
  )
31
41
  from rasa.shared.constants import (
32
- DOCS_BASE_URL,
33
42
  DEFAULT_NLU_FALLBACK_INTENT_NAME,
43
+ DOCS_BASE_URL,
44
+ FLOW_PREFIX,
34
45
  ROUTE_TO_CALM_SLOT,
35
46
  UTTER_PREFIX,
36
- FLOW_PREFIX,
37
47
  )
38
48
  from rasa.shared.core.constants import (
39
- ACTION_RESET_ROUTING,
40
- USER_INTENT_OUT_OF_SCOPE,
49
+ ACTION_BACK_NAME,
50
+ ACTION_DEACTIVATE_LOOP_NAME,
51
+ ACTION_DEFAULT_ASK_AFFIRMATION_NAME,
52
+ ACTION_DEFAULT_ASK_REPHRASE_NAME,
53
+ ACTION_DEFAULT_FALLBACK_NAME,
54
+ ACTION_EXTRACT_SLOTS,
41
55
  ACTION_LISTEN_NAME,
56
+ ACTION_RESET_ROUTING,
42
57
  ACTION_RESTART_NAME,
58
+ ACTION_REVERT_FALLBACK_EVENTS_NAME,
43
59
  ACTION_SEND_TEXT_NAME,
44
60
  ACTION_SESSION_START_NAME,
45
- ACTION_DEFAULT_FALLBACK_NAME,
46
- ACTION_DEACTIVATE_LOOP_NAME,
47
- ACTION_REVERT_FALLBACK_EVENTS_NAME,
48
- ACTION_DEFAULT_ASK_AFFIRMATION_NAME,
49
- ACTION_DEFAULT_ASK_REPHRASE_NAME,
50
61
  ACTION_UNLIKELY_INTENT_NAME,
51
- ACTION_BACK_NAME,
52
- REQUESTED_SLOT,
53
- ACTION_EXTRACT_SLOTS,
54
- DEFAULT_SLOT_NAMES,
55
62
  ACTION_VALIDATE_SLOT_MAPPINGS,
63
+ DEFAULT_SLOT_NAMES,
64
+ KNOWLEDGE_BASE_SLOT_NAMES,
56
65
  MAPPING_TYPE,
66
+ REQUESTED_SLOT,
67
+ USER_INTENT_OUT_OF_SCOPE,
57
68
  SlotMappingType,
58
- KNOWLEDGE_BASE_SLOT_NAMES,
59
69
  )
60
70
  from rasa.shared.core.domain import Domain
61
71
  from rasa.shared.core.events import (
62
- RoutingSessionEnded,
63
- UserUtteranceReverted,
64
- UserUttered,
65
72
  ActionExecuted,
66
- Event,
67
- BotUttered,
68
- SlotSet,
69
73
  ActiveLoop,
74
+ BotUttered,
75
+ Event,
70
76
  Restarted,
77
+ RoutingSessionEnded,
71
78
  SessionStarted,
79
+ SlotSet,
80
+ UserUtteranceReverted,
81
+ UserUttered,
72
82
  )
73
83
  from rasa.shared.core.flows import FlowsList
74
84
  from rasa.shared.core.slot_mappings import (
@@ -81,28 +91,30 @@ from rasa.shared.nlu.constants import (
81
91
  INTENT_NAME_KEY,
82
92
  INTENT_RANKING_KEY,
83
93
  )
94
+ from rasa.shared.utils.io import raise_warning
84
95
  from rasa.shared.utils.schemas.events import EVENTS_SCHEMA
85
- from rasa.utils.endpoints import EndpointConfig, ClientResponseError
86
- from rasa.utils.url_tools import get_url_schema, UrlSchema
96
+ from rasa.utils.endpoints import ClientResponseError, EndpointConfig
97
+ from rasa.utils.url_tools import UrlSchema, get_url_schema
87
98
 
88
99
  if TYPE_CHECKING:
89
- from rasa.core.nlg import NaturalLanguageGenerator
90
100
  from rasa.core.channels.channel import OutputChannel
101
+ from rasa.core.nlg import NaturalLanguageGenerator
91
102
  from rasa.shared.core.events import IntentPrediction
92
103
 
104
+
93
105
  logger = logging.getLogger(__name__)
94
106
 
95
107
 
96
108
  def default_actions(action_endpoint: Optional[EndpointConfig] = None) -> List["Action"]:
97
109
  """List default actions."""
98
- from rasa.core.actions.two_stage_fallback import TwoStageFallbackAction
99
- from rasa.dialogue_understanding.patterns.correction import ActionCorrectFlowSlot
100
- from rasa.dialogue_understanding.patterns.cancel import ActionCancelFlow
101
- from rasa.dialogue_understanding.patterns.clarify import ActionClarifyFlows
110
+ from rasa.core.actions.action_clean_stack import ActionCleanStack
102
111
  from rasa.core.actions.action_run_slot_rejections import ActionRunSlotRejections
103
112
  from rasa.core.actions.action_trigger_chitchat import ActionTriggerChitchat
104
113
  from rasa.core.actions.action_trigger_search import ActionTriggerSearch
105
- from rasa.core.actions.action_clean_stack import ActionCleanStack
114
+ from rasa.core.actions.two_stage_fallback import TwoStageFallbackAction
115
+ from rasa.dialogue_understanding.patterns.cancel import ActionCancelFlow
116
+ from rasa.dialogue_understanding.patterns.clarify import ActionClarifyFlows
117
+ from rasa.dialogue_understanding.patterns.correction import ActionCorrectFlowSlot
106
118
 
107
119
  return [
108
120
  ActionListen(),
@@ -130,7 +142,9 @@ def default_actions(action_endpoint: Optional[EndpointConfig] = None) -> List["A
130
142
 
131
143
 
132
144
  def action_for_index(
133
- index: int, domain: Domain, action_endpoint: Optional[EndpointConfig]
145
+ index: int,
146
+ domain: Domain,
147
+ action_endpoint: Optional[EndpointConfig],
134
148
  ) -> "Action":
135
149
  """Get an action based on its index in the list of available actions.
136
150
 
@@ -153,7 +167,9 @@ def action_for_index(
153
167
  )
154
168
 
155
169
  return action_for_name_or_text(
156
- domain.action_names_or_texts[index], domain, action_endpoint
170
+ domain.action_names_or_texts[index],
171
+ domain,
172
+ action_endpoint,
157
173
  )
158
174
 
159
175
 
@@ -177,7 +193,9 @@ def is_retrieval_action(action_name: Text, retrieval_intents: List[Text]) -> boo
177
193
 
178
194
 
179
195
  def action_for_name_or_text(
180
- action_name_or_text: Text, domain: Domain, action_endpoint: Optional[EndpointConfig]
196
+ action_name_or_text: Text,
197
+ domain: Domain,
198
+ action_endpoint: Optional[EndpointConfig],
181
199
  ) -> "Action":
182
200
  """Retrieves an action by its name or by its text in case it's an end-to-end action.
183
201
 
@@ -347,8 +365,11 @@ class ActionBotResponse(Action):
347
365
  )
348
366
  )
349
367
  return []
350
- message["utter_action"] = self.utter_action
351
368
 
369
+ message.update(metadata or {})
370
+ message = add_bot_utterance_metadata(
371
+ message, self.utter_action, nlg, domain, tracker
372
+ )
352
373
  return [create_bot_utterance(message)]
353
374
 
354
375
  def name(self) -> Text:
@@ -702,12 +723,15 @@ class ActionDeactivateLoop(Action):
702
723
 
703
724
  class RemoteAction(Action):
704
725
  def __init__(
705
- self, name: Text, action_endpoint: Optional[EndpointConfig] = None
726
+ self,
727
+ name: Text,
728
+ action_endpoint: Optional[EndpointConfig] = None,
706
729
  ) -> None:
707
730
  self._name = name
708
731
  self.action_endpoint = action_endpoint
709
732
  self.executor = self._create_executor()
710
733
 
734
+ @lru_cache(maxsize=1)
711
735
  def _create_executor(self) -> CustomActionExecutor:
712
736
  """Creates an executor based on the action endpoint configuration.
713
737
 
@@ -717,10 +741,22 @@ class RemoteAction(Action):
717
741
  Raises:
718
742
  RasaException: If no valid action endpoint is configured.
719
743
  """
720
-
721
744
  if not self.action_endpoint:
722
745
  return NoEndpointCustomActionExecutor(self.name())
723
746
 
747
+ if self.action_endpoint.kwargs.get(KEY_STUB_CUSTOM_ACTIONS):
748
+ return E2EStubCustomActionExecutor(self.name(), self.action_endpoint)
749
+
750
+ if self.action_endpoint.url and self.action_endpoint.actions_module:
751
+ raise_warning(
752
+ "Both 'actions_module' and 'url' are defined. "
753
+ "As they are mutually exclusive and 'actions_module' "
754
+ "is prioritized, actions will be executed by the assistant."
755
+ )
756
+
757
+ if self.action_endpoint and self.action_endpoint.actions_module:
758
+ return DirectCustomActionExecutor(self.name(), self.action_endpoint)
759
+
724
760
  url_schema = get_url_schema(self.action_endpoint.url)
725
761
 
726
762
  if url_schema == UrlSchema.GRPC:
@@ -760,8 +796,7 @@ class RemoteAction(Action):
760
796
  return schema
761
797
 
762
798
  def _validate_action_result(self, result: Dict[Text, Any]) -> bool:
763
- from jsonschema import validate
764
- from jsonschema import ValidationError
799
+ from jsonschema import ValidationError, validate
765
800
 
766
801
  try:
767
802
  validate(result, self.action_response_format_spec())
@@ -781,20 +816,25 @@ class RemoteAction(Action):
781
816
  output_channel: "OutputChannel",
782
817
  nlg: "NaturalLanguageGenerator",
783
818
  tracker: "DialogueStateTracker",
819
+ **kwargs: Any,
784
820
  ) -> List[BotUttered]:
785
821
  """Use the responses generated by the action endpoint and utter them."""
786
822
  bot_messages = []
823
+ domain: Domain = kwargs.get("domain", None)
824
+ action_name: str = kwargs.get("action_name", None)
787
825
  for response in responses:
788
826
  generated_response = response.pop("response", None)
789
- if generated_response:
827
+ if generated_response is not None:
790
828
  draft = await nlg.generate(
791
829
  generated_response, tracker, output_channel.name(), **response
792
830
  )
793
831
  if not draft:
794
832
  continue
795
- draft["utter_action"] = generated_response
833
+ draft = add_bot_utterance_metadata(
834
+ draft, generated_response, nlg, domain, tracker
835
+ )
796
836
  else:
797
- draft = {}
837
+ draft = {UTTER_SOURCE_METADATA_KEY: action_name}
798
838
 
799
839
  buttons = response.pop("buttons", []) or []
800
840
  if buttons:
@@ -817,13 +857,21 @@ class RemoteAction(Action):
817
857
  metadata: Optional[Dict[Text, Any]] = None,
818
858
  ) -> List[Event]:
819
859
  """Runs action. Please see parent class for the full docstring."""
820
- response = await self.executor.run(tracker=tracker, domain=domain)
860
+ response = await self.executor.run(
861
+ domain=domain,
862
+ tracker=tracker,
863
+ )
821
864
  self._validate_action_result(response)
822
865
 
823
866
  events_json = response.get("events", [])
824
867
  responses = response.get("responses", [])
825
868
  bot_messages = await self._utter_responses(
826
- responses, output_channel, nlg, tracker
869
+ responses,
870
+ output_channel,
871
+ nlg,
872
+ tracker,
873
+ domain=domain,
874
+ action_name=self.name(),
827
875
  )
828
876
 
829
877
  events = rasa.shared.core.events.deserialise_events(events_json)
@@ -1029,7 +1077,8 @@ class ActionSendText(Action):
1029
1077
  ) -> List[Event]:
1030
1078
  """Runs action. Please see parent class for the full docstring."""
1031
1079
  fallback = {"text": ""}
1032
- message = metadata.get("message", fallback) if metadata else fallback
1080
+ metadata_copy = copy.deepcopy(metadata) if metadata else {}
1081
+ message = metadata_copy.get("message", fallback)
1033
1082
  return [create_bot_utterance(message)]
1034
1083
 
1035
1084
 
@@ -5,6 +5,7 @@ from jinja2 import Template
5
5
  from pypred import Predicate
6
6
 
7
7
  from rasa.core.actions.action import Action, create_bot_utterance
8
+ from rasa.core.utils import add_bot_utterance_metadata
8
9
  from rasa.dialogue_understanding.patterns.collect_information import (
9
10
  CollectInformationPatternFlowStackFrame,
10
11
  )
@@ -201,7 +202,9 @@ class ActionRunSlotRejections(Action):
201
202
  utterance=utterance,
202
203
  )
203
204
  else:
204
- message["utter_action"] = utterance
205
+ message = add_bot_utterance_metadata(
206
+ message, utterance, nlg, domain, tracker
207
+ )
205
208
  events.append(create_bot_utterance(message))
206
209
 
207
210
  return events
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import abc
2
4
  import logging
3
5
  from typing import TYPE_CHECKING, Any, Dict, Text
@@ -36,7 +38,7 @@ class CustomActionExecutor(abc.ABC):
36
38
  Args:
37
39
  tracker: The current state of the dialogue.
38
40
  domain: The domain object containing domain-specific information.
39
- include_domain: If True, the domain information is included in the request.
41
+ include_domain: If True, the domain is included in the request.
40
42
 
41
43
  Returns:
42
44
  The response from the execution of the custom action.
@@ -71,8 +73,7 @@ class NoEndpointCustomActionExecutor(CustomActionExecutor):
71
73
  Args:
72
74
  tracker: The current state of the dialogue.
73
75
  domain: The domain object containing domain-specific information.
74
- include_domain: If True, the domain information
75
- is included in the request.
76
+ include_domain: If True, the domain is included in the request.
76
77
 
77
78
  Returns:
78
79
  The response from the execution of the custom action.
@@ -123,7 +124,7 @@ class CustomActionRequestWriter:
123
124
  Args:
124
125
  tracker: The current state of the dialogue.
125
126
  domain: The domain object containing domain-specific information.
126
- include_domain: If True, the domain information is included in the request.
127
+ include_domain: If True, the domain is included in the request.
127
128
 
128
129
  Returns:
129
130
  A JSON payload to be sent to the action server.
@@ -173,14 +174,16 @@ class RetryCustomActionExecutor(CustomActionExecutor):
173
174
  Args:
174
175
  tracker: The current state of the dialogue.
175
176
  domain: The domain object containing domain-specific information.
176
- include_domain: If True, the domain information is included in the request
177
+ include_domain: If True, the domain is included in the request.
177
178
 
178
179
  Returns:
179
180
  The response from the execution of the custom action.
180
181
  """
181
182
  try:
182
183
  return await self._custom_action_executor.run(
183
- tracker, domain, include_domain=include_domain
184
+ tracker,
185
+ domain,
186
+ include_domain=include_domain,
184
187
  )
185
188
  except DomainNotFound:
186
189
  return await self._custom_action_executor.run(
@@ -0,0 +1,80 @@
1
+ from importlib.util import find_spec
2
+ from typing import (
3
+ Any,
4
+ Dict,
5
+ Text,
6
+ )
7
+
8
+ import structlog
9
+ from rasa_sdk.executor import ActionExecutor
10
+
11
+ import rasa
12
+ from rasa.core.actions.custom_action_executor import (
13
+ CustomActionExecutor,
14
+ )
15
+ from rasa.shared.core.domain import Domain
16
+ from rasa.shared.core.trackers import DialogueStateTracker, EventVerbosity
17
+ from rasa.shared.exceptions import RasaException
18
+ from rasa.utils.endpoints import EndpointConfig
19
+
20
+ structlogger = structlog.get_logger(__name__)
21
+
22
+
23
+ class DirectCustomActionExecutor(CustomActionExecutor):
24
+ def __init__(self, action_name: str, action_endpoint: EndpointConfig):
25
+ """Initializes the direct custom action executor.
26
+
27
+ Args:
28
+ action_name: Name of the custom action.
29
+ action_endpoint: The endpoint to execute custom actions.
30
+ """
31
+ self.action_name = action_name
32
+ self.action_endpoint = action_endpoint
33
+ self.action_executor = ActionExecutor()
34
+
35
+ def register_actions_from_a_module(self) -> None:
36
+ module_name = self.action_endpoint.actions_module
37
+ if not find_spec(module_name):
38
+ raise RasaException(
39
+ f"You've provided the custom actions module '{module_name}' "
40
+ f"to run directly by the rasa server, however this module does "
41
+ f"not exist. Please check for typos in your `endpoints.yml` file."
42
+ )
43
+
44
+ self.action_executor.register_package(module_name)
45
+
46
+ async def run(
47
+ self,
48
+ tracker: "DialogueStateTracker",
49
+ domain: "Domain",
50
+ include_domain: bool = False,
51
+ ) -> Dict[Text, Any]:
52
+ """Executes the custom action directly.
53
+
54
+ Args:
55
+ tracker: The current state of the dialogue.
56
+ domain: The domain object containing domain-specific information.
57
+ include_domain: If True, the domain is included in the request.
58
+
59
+ Returns:
60
+ The response from the execution of the custom action.
61
+ """
62
+ structlogger.debug(
63
+ "action.direct_custom_action_executor.run",
64
+ action_name=self.action_name,
65
+ )
66
+ self.register_actions_from_a_module()
67
+
68
+ tracker_state = tracker.current_state(EventVerbosity.ALL)
69
+ action_call = {
70
+ "next_action": self.action_name,
71
+ "sender_id": tracker.sender_id,
72
+ "tracker": tracker_state,
73
+ "version": rasa.__version__,
74
+ }
75
+
76
+ if domain:
77
+ action_call["domain"] = domain.as_dict()
78
+
79
+ result = await self.action_executor.run(action_call)
80
+ return result.model_dump() if result else {}