rasa-pro 3.12.0.dev12__py3-none-any.whl → 3.12.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.

Files changed (153) hide show
  1. rasa/anonymization/anonymization_rule_executor.py +16 -10
  2. rasa/cli/data.py +16 -0
  3. rasa/cli/inspect.py +20 -1
  4. rasa/cli/project_templates/calm/config.yml +2 -2
  5. rasa/cli/project_templates/calm/endpoints.yml +2 -2
  6. rasa/cli/shell.py +3 -3
  7. rasa/cli/utils.py +12 -0
  8. rasa/core/actions/action.py +99 -193
  9. rasa/core/actions/action_handle_digressions.py +142 -0
  10. rasa/core/actions/action_run_slot_rejections.py +16 -4
  11. rasa/core/actions/forms.py +10 -5
  12. rasa/core/channels/__init__.py +4 -0
  13. rasa/core/channels/studio_chat.py +19 -0
  14. rasa/core/channels/telegram.py +42 -24
  15. rasa/core/channels/voice_ready/audiocodes.py +42 -23
  16. rasa/core/channels/voice_ready/utils.py +1 -1
  17. rasa/core/channels/voice_stream/asr/asr_engine.py +10 -4
  18. rasa/core/channels/voice_stream/asr/azure.py +14 -1
  19. rasa/core/channels/voice_stream/asr/deepgram.py +20 -4
  20. rasa/core/channels/voice_stream/audiocodes.py +264 -0
  21. rasa/core/channels/voice_stream/browser_audio.py +5 -1
  22. rasa/core/channels/voice_stream/call_state.py +10 -1
  23. rasa/core/channels/voice_stream/genesys.py +335 -0
  24. rasa/core/channels/voice_stream/tts/azure.py +11 -2
  25. rasa/core/channels/voice_stream/tts/cartesia.py +29 -10
  26. rasa/core/channels/voice_stream/twilio_media_streams.py +2 -1
  27. rasa/core/channels/voice_stream/voice_channel.py +25 -3
  28. rasa/core/constants.py +2 -0
  29. rasa/core/migrate.py +2 -2
  30. rasa/core/nlg/contextual_response_rephraser.py +18 -1
  31. rasa/core/nlg/generator.py +83 -15
  32. rasa/core/nlg/response.py +6 -3
  33. rasa/core/nlg/translate.py +55 -0
  34. rasa/core/policies/enterprise_search_prompt_with_citation_template.jinja2 +1 -1
  35. rasa/core/policies/flows/flow_executor.py +47 -46
  36. rasa/core/processor.py +72 -9
  37. rasa/core/run.py +4 -3
  38. rasa/dialogue_understanding/commands/can_not_handle_command.py +20 -2
  39. rasa/dialogue_understanding/commands/cancel_flow_command.py +80 -4
  40. rasa/dialogue_understanding/commands/change_flow_command.py +20 -2
  41. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +20 -2
  42. rasa/dialogue_understanding/commands/clarify_command.py +29 -3
  43. rasa/dialogue_understanding/commands/command.py +1 -16
  44. rasa/dialogue_understanding/commands/command_syntax_manager.py +55 -0
  45. rasa/dialogue_understanding/commands/correct_slots_command.py +11 -2
  46. rasa/dialogue_understanding/commands/handle_digressions_command.py +150 -0
  47. rasa/dialogue_understanding/commands/human_handoff_command.py +20 -2
  48. rasa/dialogue_understanding/commands/knowledge_answer_command.py +20 -2
  49. rasa/dialogue_understanding/commands/prompt_command.py +94 -0
  50. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +20 -2
  51. rasa/dialogue_understanding/commands/set_slot_command.py +29 -15
  52. rasa/dialogue_understanding/commands/skip_question_command.py +20 -2
  53. rasa/dialogue_understanding/commands/start_flow_command.py +61 -2
  54. rasa/dialogue_understanding/commands/utils.py +98 -4
  55. rasa/dialogue_understanding/constants.py +1 -0
  56. rasa/dialogue_understanding/generator/__init__.py +2 -0
  57. rasa/dialogue_understanding/generator/command_generator.py +110 -73
  58. rasa/dialogue_understanding/generator/command_parser.py +16 -13
  59. rasa/dialogue_understanding/generator/constants.py +3 -0
  60. rasa/dialogue_understanding/generator/llm_based_command_generator.py +170 -5
  61. rasa/dialogue_understanding/generator/llm_command_generator.py +5 -3
  62. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +26 -4
  63. rasa/dialogue_understanding/generator/nlu_command_adapter.py +44 -3
  64. rasa/dialogue_understanding/generator/prompt_templates/__init__.py +0 -0
  65. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_template.jinja2 +60 -0
  66. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +77 -0
  67. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_default.jinja2 +68 -0
  68. rasa/dialogue_understanding/generator/{single_step/command_prompt_template.jinja2 → prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2} +1 -1
  69. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +460 -0
  70. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +12 -318
  71. rasa/dialogue_understanding/generator/utils.py +32 -1
  72. rasa/dialogue_understanding/patterns/collect_information.py +1 -1
  73. rasa/dialogue_understanding/patterns/correction.py +13 -1
  74. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +78 -2
  75. rasa/dialogue_understanding/patterns/handle_digressions.py +81 -0
  76. rasa/dialogue_understanding/patterns/validate_slot.py +65 -0
  77. rasa/dialogue_understanding/processor/command_processor.py +154 -28
  78. rasa/dialogue_understanding/utils.py +31 -0
  79. rasa/dialogue_understanding_test/README.md +50 -0
  80. rasa/dialogue_understanding_test/du_test_case.py +28 -8
  81. rasa/dialogue_understanding_test/du_test_result.py +13 -9
  82. rasa/dialogue_understanding_test/io.py +14 -0
  83. rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +3 -3
  84. rasa/e2e_test/utils/io.py +0 -37
  85. rasa/engine/graph.py +1 -0
  86. rasa/engine/language.py +140 -0
  87. rasa/engine/recipes/config_files/default_config.yml +4 -0
  88. rasa/engine/recipes/default_recipe.py +2 -0
  89. rasa/engine/recipes/graph_recipe.py +2 -0
  90. rasa/engine/storage/local_model_storage.py +1 -0
  91. rasa/engine/storage/storage.py +4 -1
  92. rasa/model_manager/runner_service.py +7 -4
  93. rasa/model_manager/socket_bridge.py +7 -6
  94. rasa/model_manager/warm_rasa_process.py +0 -1
  95. rasa/model_training.py +24 -27
  96. rasa/shared/constants.py +15 -13
  97. rasa/shared/core/constants.py +30 -3
  98. rasa/shared/core/domain.py +13 -20
  99. rasa/shared/core/events.py +13 -2
  100. rasa/shared/core/flows/constants.py +11 -0
  101. rasa/shared/core/flows/flow.py +100 -19
  102. rasa/shared/core/flows/flows_yaml_schema.json +69 -3
  103. rasa/shared/core/flows/steps/collect.py +19 -37
  104. rasa/shared/core/flows/utils.py +43 -4
  105. rasa/shared/core/flows/validation.py +1 -1
  106. rasa/shared/core/slot_mappings.py +350 -111
  107. rasa/shared/core/slots.py +154 -3
  108. rasa/shared/core/trackers.py +77 -2
  109. rasa/shared/importers/importer.py +50 -2
  110. rasa/shared/nlu/constants.py +1 -0
  111. rasa/shared/nlu/training_data/schemas/responses.yml +19 -12
  112. rasa/shared/providers/_configs/azure_entra_id_config.py +541 -0
  113. rasa/shared/providers/_configs/azure_openai_client_config.py +138 -3
  114. rasa/shared/providers/_configs/client_config.py +3 -1
  115. rasa/shared/providers/_configs/default_litellm_client_config.py +3 -1
  116. rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +3 -1
  117. rasa/shared/providers/_configs/litellm_router_client_config.py +3 -1
  118. rasa/shared/providers/_configs/model_group_config.py +4 -2
  119. rasa/shared/providers/_configs/oauth_config.py +33 -0
  120. rasa/shared/providers/_configs/openai_client_config.py +3 -1
  121. rasa/shared/providers/_configs/rasa_llm_client_config.py +3 -1
  122. rasa/shared/providers/_configs/self_hosted_llm_client_config.py +3 -1
  123. rasa/shared/providers/constants.py +6 -0
  124. rasa/shared/providers/embedding/azure_openai_embedding_client.py +28 -3
  125. rasa/shared/providers/embedding/litellm_router_embedding_client.py +3 -1
  126. rasa/shared/providers/llm/_base_litellm_client.py +42 -17
  127. rasa/shared/providers/llm/azure_openai_llm_client.py +81 -25
  128. rasa/shared/providers/llm/default_litellm_llm_client.py +3 -1
  129. rasa/shared/providers/llm/litellm_router_llm_client.py +29 -8
  130. rasa/shared/providers/llm/llm_client.py +23 -7
  131. rasa/shared/providers/llm/openai_llm_client.py +9 -3
  132. rasa/shared/providers/llm/rasa_llm_client.py +11 -2
  133. rasa/shared/providers/llm/self_hosted_llm_client.py +30 -11
  134. rasa/shared/providers/router/_base_litellm_router_client.py +3 -1
  135. rasa/shared/providers/router/router_client.py +3 -1
  136. rasa/shared/utils/constants.py +3 -0
  137. rasa/shared/utils/llm.py +31 -8
  138. rasa/shared/utils/pykwalify_extensions.py +24 -0
  139. rasa/shared/utils/schemas/domain.yml +26 -1
  140. rasa/telemetry.py +45 -14
  141. rasa/tracing/config.py +2 -0
  142. rasa/tracing/constants.py +12 -0
  143. rasa/tracing/instrumentation/instrumentation.py +36 -0
  144. rasa/tracing/instrumentation/metrics.py +41 -0
  145. rasa/tracing/metric_instrument_provider.py +40 -0
  146. rasa/utils/common.py +0 -1
  147. rasa/validator.py +561 -89
  148. rasa/version.py +1 -1
  149. {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0rc1.dist-info}/METADATA +2 -1
  150. {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0rc1.dist-info}/RECORD +153 -134
  151. {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0rc1.dist-info}/NOTICE +0 -0
  152. {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0rc1.dist-info}/WHEEL +0 -0
  153. {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0rc1.dist-info}/entry_points.txt +0 -0
@@ -4,20 +4,14 @@ from dataclasses import dataclass
4
4
  from typing import Any, Dict, List, Optional, Text, Union
5
5
 
6
6
  from faker import Faker
7
- from presidio_analyzer import AnalyzerEngine
8
- from presidio_analyzer.nlp_engine import (
9
- SpacyNlpEngine,
10
- StanzaNlpEngine,
11
- TransformersNlpEngine,
12
- )
13
- from presidio_anonymizer import AnonymizerEngine
14
- from presidio_anonymizer.entities import OperatorConfig
15
7
 
16
8
  from rasa.shared.exceptions import RasaException
17
9
  from rasa.utils.singleton import Singleton
18
10
 
19
11
  if typing.TYPE_CHECKING:
12
+ from presidio_analyzer import AnalyzerEngine
20
13
  from presidio_analyzer.nlp_engine.nlp_engine import NlpEngine
14
+ from presidio_anonymizer.entities import OperatorConfig
21
15
 
22
16
  DEFAULT_PRESIDIO_LANG_CODE = "en"
23
17
  DEFAULT_PRESIDIO_MODEL_NAME = "en_core_web_lg"
@@ -61,8 +55,10 @@ class AnonymizationAnalyzer(metaclass=Singleton):
61
55
  @staticmethod
62
56
  def _get_analyzer_engine(
63
57
  anonymization_rule_list: AnonymizationRuleList,
64
- ) -> AnalyzerEngine:
58
+ ) -> "AnalyzerEngine":
65
59
  """Returns an analyzer engine for all the anonymization rule lists."""
60
+ from presidio_analyzer import AnalyzerEngine
61
+
66
62
  try:
67
63
  nlp_engine = AnonymizationAnalyzer._build_presidio_nlp_engine(
68
64
  anonymization_rule_list
@@ -84,6 +80,12 @@ class AnonymizationAnalyzer(metaclass=Singleton):
84
80
  anonymization_rule_list: AnonymizationRuleList,
85
81
  ) -> "NlpEngine":
86
82
  """Creates an instance of the Presidio nlp engine."""
83
+ from presidio_analyzer.nlp_engine import (
84
+ SpacyNlpEngine,
85
+ StanzaNlpEngine,
86
+ TransformersNlpEngine,
87
+ )
88
+
87
89
  if anonymization_rule_list.model_provider == "transformers":
88
90
  nlp_engine = TransformersNlpEngine(
89
91
  models={
@@ -111,6 +113,8 @@ class AnonymizationRuleExecutor:
111
113
 
112
114
  def __init__(self, anonymization_rule_list: AnonymizationRuleList):
113
115
  """Initialize the anonymization rule executor."""
116
+ from presidio_anonymizer import AnonymizerEngine
117
+
114
118
  self.anonymization_rule_list = anonymization_rule_list
115
119
 
116
120
  is_valid_rule_list = self._validate_anonymization_rule_list(
@@ -243,8 +247,10 @@ class AnonymizationRuleExecutor:
243
247
 
244
248
  return func
245
249
 
246
- def get_operators(self) -> Dict[Text, OperatorConfig]:
250
+ def get_operators(self) -> Dict[Text, "OperatorConfig"]:
247
251
  """Returns a dictionary of operators for the given anonymization rule list."""
252
+ from presidio_anonymizer.entities import OperatorConfig
253
+
248
254
  operators = {}
249
255
 
250
256
  for rule in self.anonymization_rule_list.rule_list:
rasa/cli/data.py CHANGED
@@ -178,6 +178,22 @@ def _add_data_validate_parsers(
178
178
  )
179
179
  arguments.set_validator_arguments(flows_structure_parser)
180
180
 
181
+ translations_structure_parser = validate_subparsers.add_parser(
182
+ "translations",
183
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
184
+ parents=parents,
185
+ help="Checks for inconsistencies of the flow and response translation.",
186
+ )
187
+ translations_structure_parser.set_defaults(
188
+ func=lambda args: rasa.cli.utils.validate_files(
189
+ args.fail_on_warnings,
190
+ args.max_history,
191
+ _build_training_data_importer(args),
192
+ translations_only=True,
193
+ )
194
+ )
195
+ arguments.set_validator_arguments(translations_structure_parser)
196
+
181
197
 
182
198
  def _build_training_data_importer(args: argparse.Namespace) -> "TrainingDataImporter":
183
199
  config = rasa.cli.utils.get_validated_path(
rasa/cli/inspect.py CHANGED
@@ -9,6 +9,10 @@ from rasa import telemetry
9
9
  from rasa.cli import SubParsersAction
10
10
  from rasa.cli.arguments import shell as arguments
11
11
  from rasa.core import constants
12
+ from rasa.engine.storage.local_model_storage import LocalModelStorage
13
+ from rasa.exceptions import ModelNotFound
14
+ from rasa.model import get_local_model
15
+ from rasa.shared.utils.cli import print_error
12
16
  from rasa.utils.cli import remove_argument_from_parser
13
17
 
14
18
 
@@ -55,6 +59,8 @@ async def open_inspector_in_browser(server_url: Text, voice: bool = False) -> No
55
59
  def inspect(args: argparse.Namespace) -> None:
56
60
  """Inspect the bot using the most recent model."""
57
61
  import rasa.cli.run
62
+ from rasa.cli.utils import get_validated_path
63
+ from rasa.shared.constants import DEFAULT_MODELS_PATH
58
64
 
59
65
  async def after_start_hook_open_inspector(_: Sanic, __: AbstractEventLoop) -> None:
60
66
  """Hook to open the browser on server start."""
@@ -71,5 +77,18 @@ def inspect(args: argparse.Namespace) -> None:
71
77
  args.credentials = None
72
78
  args.server_listeners = [(after_start_hook_open_inspector, "after_server_start")]
73
79
 
74
- telemetry.track_inspect_started(args.connector)
80
+ model = get_validated_path(args.model, "model", DEFAULT_MODELS_PATH)
81
+
82
+ try:
83
+ model = get_local_model(model)
84
+ except ModelNotFound:
85
+ print_error(
86
+ "No model found. Train a model before running the "
87
+ "server using `rasa train`."
88
+ )
89
+ return
90
+
91
+ metadata = LocalModelStorage.metadata_from_archive(model)
92
+
93
+ telemetry.track_inspect_started(args.connector, metadata.assistant_id)
75
94
  rasa.cli.run.run(args)
@@ -1,9 +1,9 @@
1
1
  recipe: default.v1
2
2
  language: en
3
3
  pipeline:
4
- - name: SingleStepLLMCommandGenerator
4
+ - name: CompactLLMCommandGenerator
5
5
  llm:
6
- model_group: openai-gpt-4
6
+ model_group: openai-gpt-4o
7
7
 
8
8
  policies:
9
9
  - name: FlowPolicy
@@ -50,9 +50,9 @@ action_endpoint:
50
50
  # type: rephrase
51
51
 
52
52
  model_groups:
53
- - id: openai-gpt-4
53
+ - id: openai-gpt-4o
54
54
  models:
55
55
  - provider: openai
56
- model: openai/gpt-4
56
+ model: gpt-4o-2024-11-20
57
57
  request_timeout: 7
58
58
  max_tokens: 256
rasa/cli/shell.py CHANGED
@@ -95,7 +95,7 @@ def shell_nlu(args: argparse.Namespace) -> None:
95
95
  )
96
96
  return
97
97
 
98
- telemetry.track_shell_started("nlu")
98
+ telemetry.track_shell_started("nlu", metadata.assistant_id)
99
99
  rasa.nlu.run.run_cmdline(model)
100
100
 
101
101
 
@@ -129,12 +129,12 @@ def shell(args: argparse.Namespace) -> None:
129
129
  if metadata.training_type == TrainingType.NLU:
130
130
  import rasa.nlu.run
131
131
 
132
- telemetry.track_shell_started("nlu")
132
+ telemetry.track_shell_started("nlu", metadata.assistant_id)
133
133
 
134
134
  rasa.nlu.run.run_cmdline(model)
135
135
  else:
136
136
  import rasa.cli.run
137
137
 
138
- telemetry.track_shell_started("rasa")
138
+ telemetry.track_shell_started("rasa", metadata.assistant_id)
139
139
 
140
140
  rasa.cli.run.run(args)
rasa/cli/utils.py CHANGED
@@ -248,6 +248,7 @@ def validate_files(
248
248
  importer: TrainingDataImporter,
249
249
  stories_only: bool = False,
250
250
  flows_only: bool = False,
251
+ translations_only: bool = False,
251
252
  ) -> None:
252
253
  """Validates either the story structure or the entire project.
253
254
 
@@ -257,6 +258,7 @@ def validate_files(
257
258
  importer: The `TrainingDataImporter` to use to load the training data.
258
259
  stories_only: If `True`, only the story structure is validated.
259
260
  flows_only: If `True`, only the flows are validated.
261
+ translations_only: If `True`, only the translations data is validated.
260
262
  """
261
263
  from rasa.validator import Validator
262
264
 
@@ -266,6 +268,8 @@ def validate_files(
266
268
  all_good = _validate_story_structure(validator, max_history, fail_on_warnings)
267
269
  elif flows_only:
268
270
  all_good = validator.verify_flows()
271
+ elif translations_only:
272
+ all_good = validator.verify_translations()
269
273
  else:
270
274
  if importer.get_domain().is_empty():
271
275
  structlogger.error(
@@ -280,6 +284,7 @@ def validate_files(
280
284
  validator, max_history, fail_on_warnings
281
285
  )
282
286
  valid_flows = validator.verify_flows()
287
+ valid_translations = validator.verify_translations(summary_mode=True)
283
288
  valid_CALM_slot_mappings = validator.validate_CALM_slot_mappings()
284
289
 
285
290
  all_good = (
@@ -287,6 +292,7 @@ def validate_files(
287
292
  and valid_nlu
288
293
  and valid_stories
289
294
  and valid_flows
295
+ and valid_translations
290
296
  and valid_CALM_slot_mappings
291
297
  )
292
298
 
@@ -309,6 +315,10 @@ def _validate_domain(validator: "Validator") -> bool:
309
315
  valid_slot_mappings = validator.verify_slot_mappings()
310
316
  valid_responses = validator.check_for_no_empty_parenthesis_in_responses()
311
317
  valid_buttons = validator.validate_button_payloads()
318
+ valid_slot_validation = validator.verify_slot_validation()
319
+ valid_conditional_responses = (
320
+ validator.validate_conditional_response_variation_predicates()
321
+ )
312
322
  return (
313
323
  valid_domain_validity
314
324
  and valid_actions_in_stories_rules
@@ -317,6 +327,8 @@ def _validate_domain(validator: "Validator") -> bool:
317
327
  and valid_slot_mappings
318
328
  and valid_responses
319
329
  and valid_buttons
330
+ and valid_slot_validation
331
+ and valid_conditional_responses
320
332
  )
321
333
 
322
334
 
@@ -1,17 +1,7 @@
1
1
  import copy
2
2
  import logging
3
3
  from functools import lru_cache
4
- from typing import (
5
- TYPE_CHECKING,
6
- Any,
7
- Dict,
8
- List,
9
- Optional,
10
- Set,
11
- Text,
12
- Tuple,
13
- cast,
14
- )
4
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Text, cast
15
5
 
16
6
  from jsonschema import Draft202012Validator
17
7
 
@@ -29,11 +19,15 @@ from rasa.core.actions.e2e_stub_custom_action_executor import (
29
19
  from rasa.core.actions.grpc_custom_action_executor import GRPCCustomActionExecutor
30
20
  from rasa.core.actions.http_custom_action_executor import HTTPCustomActionExecutor
31
21
  from rasa.core.constants import (
22
+ KEY_IS_CALM_SYSTEM,
23
+ KEY_IS_COEXISTENCE_ASSISTANT,
32
24
  UTTER_SOURCE_METADATA_KEY,
33
25
  )
26
+ from rasa.core.nlg.translate import get_translated_buttons, get_translated_text
34
27
  from rasa.core.policies.policy import PolicyPrediction
35
28
  from rasa.core.utils import add_bot_utterance_metadata
36
29
  from rasa.e2e_test.constants import KEY_STUB_CUSTOM_ACTIONS
30
+ from rasa.engine.language import Language
37
31
  from rasa.nlu.constants import (
38
32
  RESPONSE_SELECTOR_DEFAULT_INTENT,
39
33
  RESPONSE_SELECTOR_PREDICTION_KEY,
@@ -70,13 +64,11 @@ from rasa.shared.core.constants import (
70
64
  ACTION_SEND_TEXT_NAME,
71
65
  ACTION_SESSION_START_NAME,
72
66
  ACTION_UNLIKELY_INTENT_NAME,
73
- ACTION_VALIDATE_SLOT_MAPPINGS,
74
67
  DEFAULT_SLOT_NAMES,
75
68
  KNOWLEDGE_BASE_SLOT_NAMES,
76
- MAPPING_TYPE,
77
69
  REQUESTED_SLOT,
78
70
  USER_INTENT_OUT_OF_SCOPE,
79
- SlotMappingType,
71
+ SetSlotExtractor,
80
72
  )
81
73
  from rasa.shared.core.domain import Domain
82
74
  from rasa.shared.core.events import (
@@ -92,19 +84,17 @@ from rasa.shared.core.events import (
92
84
  UserUttered,
93
85
  )
94
86
  from rasa.shared.core.flows import FlowsList
87
+ from rasa.shared.core.flows.constants import KEY_TRANSLATION
95
88
  from rasa.shared.core.slot_mappings import (
96
89
  SlotFillingManager,
97
90
  extract_slot_value,
98
91
  )
99
92
  from rasa.shared.core.trackers import DialogueStateTracker
100
93
  from rasa.shared.exceptions import RasaException
101
- from rasa.shared.nlu.constants import (
102
- INTENT_NAME_KEY,
103
- INTENT_RANKING_KEY,
104
- )
94
+ from rasa.shared.nlu.constants import INTENT_NAME_KEY, INTENT_RANKING_KEY
105
95
  from rasa.shared.utils.io import raise_warning
106
96
  from rasa.shared.utils.schemas.events import EVENTS_SCHEMA
107
- from rasa.utils.endpoints import ClientResponseError, EndpointConfig
97
+ from rasa.utils.endpoints import EndpointConfig
108
98
  from rasa.utils.url_tools import UrlSchema, get_url_schema
109
99
 
110
100
  if TYPE_CHECKING:
@@ -118,6 +108,10 @@ logger = logging.getLogger(__name__)
118
108
  def default_actions(action_endpoint: Optional[EndpointConfig] = None) -> List["Action"]:
119
109
  """List default actions."""
120
110
  from rasa.core.actions.action_clean_stack import ActionCleanStack
111
+ from rasa.core.actions.action_handle_digressions import (
112
+ ActionBlockDigressions,
113
+ ActionContinueDigression,
114
+ )
121
115
  from rasa.core.actions.action_hangup import ActionHangup
122
116
  from rasa.core.actions.action_repeat_bot_messages import ActionRepeatBotMessages
123
117
  from rasa.core.actions.action_run_slot_rejections import ActionRunSlotRejections
@@ -152,6 +146,8 @@ def default_actions(action_endpoint: Optional[EndpointConfig] = None) -> List["A
152
146
  ActionResetRouting(),
153
147
  ActionHangup(),
154
148
  ActionRepeatBotMessages(),
149
+ ActionBlockDigressions(),
150
+ ActionContinueDigression(),
155
151
  ]
156
152
 
157
153
 
@@ -261,25 +257,36 @@ def action_for_name_or_text(
261
257
  return RemoteAction(action_name_or_text, action_endpoint)
262
258
 
263
259
 
264
- def create_bot_utterance(message: Dict[Text, Any]) -> BotUttered:
265
- """Create BotUttered event from message."""
266
- bot_message = BotUttered(
267
- text=message.pop(TEXT, None),
268
- data={
269
- ELEMENTS: message.pop(ELEMENTS, None),
270
- QUICK_REPLIES: message.pop(QUICK_REPLIES, None),
271
- BUTTONS: message.pop(BUTTONS, None),
272
- # for legacy / compatibility reasons we need to set the image
273
- # to be the attachment if there is no other attachment (the
274
- # `.get` is intentional - no `pop` as we still need the image`
275
- # property to set it in the following line)
276
- ATTACHMENT: message.pop(ATTACHMENT, None) or message.get(IMAGE, None),
277
- IMAGE: message.pop(IMAGE, None),
278
- CUSTOM: message.pop(CUSTOM, None),
279
- },
280
- metadata=message,
260
+ def create_bot_utterance(
261
+ message: Dict[Text, Any], language: Optional[Language] = None
262
+ ) -> BotUttered:
263
+ """Create BotUttered event from message with translation support."""
264
+ message_copy = copy.deepcopy(message)
265
+
266
+ text = get_translated_text(
267
+ text=message_copy.pop(TEXT, None),
268
+ translation=message_copy.pop(KEY_TRANSLATION, {}),
269
+ language=language,
281
270
  )
282
- return bot_message
271
+
272
+ buttons = get_translated_buttons(
273
+ buttons=message_copy.pop(BUTTONS, None), language=language
274
+ )
275
+
276
+ data = {
277
+ ELEMENTS: message_copy.pop(ELEMENTS, None),
278
+ QUICK_REPLIES: message_copy.pop(QUICK_REPLIES, None),
279
+ BUTTONS: buttons,
280
+ # for legacy / compatibility reasons we need to set the image
281
+ # to be the attachment if there is no other attachment (the
282
+ # `.get` is intentional - no `pop` as we still need the image`
283
+ # property to set it in the following line)
284
+ ATTACHMENT: message_copy.pop(ATTACHMENT, None) or message_copy.get(IMAGE, None),
285
+ IMAGE: message_copy.pop(IMAGE, None),
286
+ CUSTOM: message_copy.pop(CUSTOM, None),
287
+ }
288
+
289
+ return BotUttered(text=text, data=data, metadata=message_copy)
283
290
 
284
291
 
285
292
  class Action:
@@ -392,7 +399,7 @@ class ActionBotResponse(Action):
392
399
  message = add_bot_utterance_metadata(
393
400
  message, self.utter_action, nlg, domain, tracker
394
401
  )
395
- return [create_bot_utterance(message)]
402
+ return [create_bot_utterance(message, tracker.current_language)]
396
403
 
397
404
  def name(self) -> Text:
398
405
  """Returns action name."""
@@ -426,7 +433,7 @@ class ActionEndToEndResponse(Action):
426
433
  ) -> List[Event]:
427
434
  """Runs action (see parent class for full docstring)."""
428
435
  message = {"text": self.action_text}
429
- return [create_bot_utterance(message)]
436
+ return [create_bot_utterance(message, tracker.current_language)]
430
437
 
431
438
  def event_for_successful_execution(
432
439
  self,
@@ -910,7 +917,7 @@ class RemoteAction(Action):
910
917
  # Avoid overwriting `draft` values with empty values
911
918
  response = {k: v for k, v in response.items() if v}
912
919
  draft.update(response)
913
- bot_messages.append(create_bot_utterance(draft))
920
+ bot_messages.append(create_bot_utterance(draft, tracker.current_language))
914
921
 
915
922
  return bot_messages
916
923
 
@@ -940,7 +947,14 @@ class RemoteAction(Action):
940
947
  )
941
948
 
942
949
  events = rasa.shared.core.events.deserialise_events(events_json)
943
- return cast(List[Event], bot_messages) + events
950
+
951
+ processed_events = []
952
+ for event in events:
953
+ if isinstance(event, SlotSet) and event.filled_by is None:
954
+ event.filled_by = SetSlotExtractor.CUSTOM.value
955
+ processed_events.append(event)
956
+
957
+ return cast(List[Event], bot_messages) + processed_events
944
958
 
945
959
  def name(self) -> Text:
946
960
  return self._name
@@ -1111,7 +1125,7 @@ class ActionDefaultAskAffirmation(Action):
1111
1125
  "utter_action": self.name(),
1112
1126
  }
1113
1127
 
1114
- return [create_bot_utterance(message)]
1128
+ return [create_bot_utterance(message, tracker.current_language)]
1115
1129
 
1116
1130
 
1117
1131
  class ActionDefaultAskRephrase(ActionBotResponse):
@@ -1144,7 +1158,7 @@ class ActionSendText(Action):
1144
1158
  fallback = {"text": ""}
1145
1159
  metadata_copy = copy.deepcopy(metadata) if metadata else {}
1146
1160
  message = metadata_copy.get("message", fallback)
1147
- return [create_bot_utterance(message)]
1161
+ return [create_bot_utterance(message, tracker.current_language)]
1148
1162
 
1149
1163
 
1150
1164
  class ActionExtractSlots(Action):
@@ -1165,113 +1179,6 @@ class ActionExtractSlots(Action):
1165
1179
  """Returns action_extract_slots name."""
1166
1180
  return ACTION_EXTRACT_SLOTS
1167
1181
 
1168
- async def _run_custom_action(
1169
- self,
1170
- custom_action: Text,
1171
- output_channel: "OutputChannel",
1172
- nlg: "NaturalLanguageGenerator",
1173
- tracker: "DialogueStateTracker",
1174
- domain: "Domain",
1175
- ) -> List[Event]:
1176
- slot_events: List[Event] = []
1177
- remote_action = RemoteAction(custom_action, self._action_endpoint)
1178
- disallowed_types = set()
1179
-
1180
- try:
1181
- custom_events = await remote_action.run(
1182
- output_channel, nlg, tracker, domain
1183
- )
1184
- for event in custom_events:
1185
- if isinstance(event, SlotSet):
1186
- slot_events.append(event)
1187
- elif isinstance(event, BotUttered):
1188
- slot_events.append(event)
1189
- else:
1190
- disallowed_types.add(event.type_name)
1191
- except (RasaException, ClientResponseError) as e:
1192
- logger.warning(
1193
- f"Failed to execute custom action '{custom_action}' "
1194
- f"as a result of error '{e!s}'. The default action "
1195
- f"'{self.name()}' failed to fill slots with custom "
1196
- f"mappings."
1197
- )
1198
-
1199
- for type_name in disallowed_types:
1200
- logger.info(
1201
- f"Running custom action '{custom_action}' has resulted "
1202
- f"in an event of type '{type_name}'. This is "
1203
- f"disallowed and the tracker will not be "
1204
- f"updated with this event."
1205
- )
1206
-
1207
- return slot_events
1208
-
1209
- async def _execute_custom_action(
1210
- self,
1211
- mapping: Dict[Text, Any],
1212
- executed_custom_actions: Set[Text],
1213
- output_channel: "OutputChannel",
1214
- nlg: "NaturalLanguageGenerator",
1215
- tracker: "DialogueStateTracker",
1216
- domain: "Domain",
1217
- calm_custom_action_names: Optional[Set[str]] = None,
1218
- ) -> Tuple[List[Event], Set[Text]]:
1219
- custom_action = mapping.get("action")
1220
-
1221
- if not custom_action or custom_action in executed_custom_actions:
1222
- return [], executed_custom_actions
1223
-
1224
- if (
1225
- calm_custom_action_names is not None
1226
- and custom_action in calm_custom_action_names
1227
- ):
1228
- return [], executed_custom_actions
1229
-
1230
- slot_events = await self._run_custom_action(
1231
- custom_action, output_channel, nlg, tracker, domain
1232
- )
1233
-
1234
- executed_custom_actions.add(custom_action)
1235
-
1236
- return slot_events, executed_custom_actions
1237
-
1238
- async def _execute_validation_action(
1239
- self,
1240
- extraction_events: List[Event],
1241
- output_channel: "OutputChannel",
1242
- nlg: "NaturalLanguageGenerator",
1243
- tracker: "DialogueStateTracker",
1244
- domain: "Domain",
1245
- ) -> List[Event]:
1246
- slot_events: List[SlotSet] = [
1247
- event for event in extraction_events if isinstance(event, SlotSet)
1248
- ]
1249
-
1250
- slot_candidates = "\n".join([e.key for e in slot_events])
1251
- logger.debug(f"Validating extracted slots: {slot_candidates}")
1252
-
1253
- if ACTION_VALIDATE_SLOT_MAPPINGS not in domain.user_actions:
1254
- return cast(List[Event], slot_events)
1255
-
1256
- _tracker = DialogueStateTracker.from_events(
1257
- tracker.sender_id,
1258
- tracker.events_after_latest_restart() + cast(List[Event], slot_events),
1259
- slots=domain.slots,
1260
- )
1261
- validate_events = await self._run_custom_action(
1262
- ACTION_VALIDATE_SLOT_MAPPINGS, output_channel, nlg, _tracker, domain
1263
- )
1264
- validated_slot_names = [
1265
- event.key for event in validate_events if isinstance(event, SlotSet)
1266
- ]
1267
-
1268
- # If the custom action doesn't return a SlotSet event for an extracted slot
1269
- # candidate we assume that it was valid. The custom action has to return a
1270
- # SlotSet(slot_name, None) event to mark a Slot as invalid.
1271
- return validate_events + [
1272
- event for event in slot_events if event.key not in validated_slot_names
1273
- ]
1274
-
1275
1182
  async def run(
1276
1183
  self,
1277
1184
  output_channel: "OutputChannel",
@@ -1282,60 +1189,59 @@ class ActionExtractSlots(Action):
1282
1189
  ) -> List[Event]:
1283
1190
  """Runs action. Please see parent class for the full docstring."""
1284
1191
  slot_events: List[Event] = []
1285
- executed_custom_actions: Set[Text] = set()
1286
-
1287
1192
  user_slots = [
1288
1193
  slot
1289
1194
  for slot in domain.slots
1290
1195
  if slot.name not in DEFAULT_SLOT_NAMES | KNOWLEDGE_BASE_SLOT_NAMES
1291
1196
  ]
1292
1197
 
1293
- calm_slot_names = set()
1294
- calm_custom_action_names = None
1295
- flows = None
1296
-
1297
- if metadata is not None:
1298
- flows = metadata.get("all_flows")
1299
-
1300
- if flows is not None:
1301
- flows = FlowsList.from_json(flows)
1302
- calm_slot_names = flows.available_slot_names()
1303
- calm_custom_action_names = flows.available_custom_actions()
1304
-
1198
+ all_flows = metadata.get("all_flows") if metadata else None
1199
+ flows = FlowsList.from_json(all_flows) if all_flows else None
1200
+ calm_slot_names = flows.available_slot_names() if flows else set()
1201
+ is_calm_system = metadata.get(KEY_IS_CALM_SYSTEM) if metadata else False
1202
+ is_coexistence_bot = (
1203
+ metadata.get(KEY_IS_COEXISTENCE_ASSISTANT) if metadata else False
1204
+ )
1305
1205
  slot_filling_manager = SlotFillingManager(
1306
- domain, tracker, action_endpoint=self._action_endpoint
1206
+ domain,
1207
+ tracker,
1208
+ action_endpoint=self._action_endpoint,
1307
1209
  )
1308
1210
 
1309
1211
  for slot in user_slots:
1310
1212
  # allows the action to set slots that are shared between
1311
1213
  # the NLU-based system and CALM system in a coexistence bot
1312
- if slot.name in calm_slot_names and not slot.shared_for_coexistence:
1313
- continue
1314
-
1315
- slot_value, is_extracted = extract_slot_value(slot, slot_filling_manager)
1316
- if is_extracted:
1317
- slot_events.append(SlotSet(slot.name, slot_value))
1318
-
1319
- for mapping in slot.mappings:
1320
- mapping_type = SlotMappingType(mapping.get(MAPPING_TYPE))
1321
- should_fill_custom_slot = mapping_type == SlotMappingType.CUSTOM
1322
-
1323
- if should_fill_custom_slot:
1324
- (
1325
- custom_evts,
1326
- executed_custom_actions,
1327
- ) = await self._execute_custom_action(
1328
- mapping,
1329
- executed_custom_actions,
1330
- output_channel,
1331
- nlg,
1332
- tracker,
1333
- domain,
1334
- calm_custom_action_names,
1214
+ if is_coexistence_bot:
1215
+ should_fill_slot_in_coexistence = (
1216
+ slot_filling_manager.should_fill_slot_in_coexistence(
1217
+ is_calm_system=is_calm_system,
1218
+ slot=slot,
1219
+ calm_slot_names=calm_slot_names,
1335
1220
  )
1336
- slot_events.extend(custom_evts)
1221
+ )
1222
+ if not should_fill_slot_in_coexistence:
1223
+ continue
1337
1224
 
1338
- validated_events = await self._execute_validation_action(
1339
- slot_events, output_channel, nlg, tracker, domain
1340
- )
1341
- return validated_events
1225
+ if not is_calm_system:
1226
+ slot_value, is_extracted = extract_slot_value(
1227
+ slot, slot_filling_manager
1228
+ )
1229
+ if is_extracted:
1230
+ slot_events.append(SlotSet(slot.name, slot_value))
1231
+
1232
+ custom_events = await slot_filling_manager.run_action_at_every_turn(
1233
+ slot,
1234
+ output_channel,
1235
+ nlg,
1236
+ )
1237
+ slot_events.extend(custom_events)
1238
+
1239
+ if not is_calm_system:
1240
+ validated_events = await slot_filling_manager.execute_validation_action(
1241
+ slot_events,
1242
+ output_channel,
1243
+ nlg,
1244
+ )
1245
+ return validated_events
1246
+
1247
+ return slot_events