rasa-pro 3.12.0.dev13__py3-none-any.whl → 3.12.0rc2__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 (139) hide show
  1. README.md +10 -13
  2. rasa/anonymization/anonymization_rule_executor.py +16 -10
  3. rasa/cli/data.py +16 -0
  4. rasa/cli/project_templates/calm/config.yml +2 -2
  5. rasa/cli/project_templates/calm/domain/list_contacts.yml +1 -2
  6. rasa/cli/project_templates/calm/domain/remove_contact.yml +1 -2
  7. rasa/cli/project_templates/calm/domain/shared.yml +1 -4
  8. rasa/cli/project_templates/calm/endpoints.yml +2 -2
  9. rasa/cli/utils.py +12 -0
  10. rasa/core/actions/action.py +84 -191
  11. rasa/core/actions/action_handle_digressions.py +35 -13
  12. rasa/core/actions/action_run_slot_rejections.py +16 -4
  13. rasa/core/channels/__init__.py +2 -0
  14. rasa/core/channels/studio_chat.py +19 -0
  15. rasa/core/channels/telegram.py +42 -24
  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 +4 -1
  22. rasa/core/channels/voice_stream/call_state.py +3 -0
  23. rasa/core/channels/voice_stream/genesys.py +6 -2
  24. rasa/core/channels/voice_stream/tts/azure.py +9 -1
  25. rasa/core/channels/voice_stream/tts/cartesia.py +14 -8
  26. rasa/core/channels/voice_stream/voice_channel.py +23 -2
  27. rasa/core/constants.py +2 -0
  28. rasa/core/nlg/contextual_response_rephraser.py +18 -1
  29. rasa/core/nlg/generator.py +83 -15
  30. rasa/core/nlg/response.py +6 -3
  31. rasa/core/nlg/translate.py +55 -0
  32. rasa/core/policies/enterprise_search_prompt_with_citation_template.jinja2 +1 -1
  33. rasa/core/policies/flows/flow_executor.py +19 -7
  34. rasa/core/processor.py +71 -9
  35. rasa/dialogue_understanding/commands/can_not_handle_command.py +20 -2
  36. rasa/dialogue_understanding/commands/cancel_flow_command.py +24 -6
  37. rasa/dialogue_understanding/commands/change_flow_command.py +20 -2
  38. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +20 -2
  39. rasa/dialogue_understanding/commands/clarify_command.py +29 -3
  40. rasa/dialogue_understanding/commands/command.py +1 -16
  41. rasa/dialogue_understanding/commands/command_syntax_manager.py +55 -0
  42. rasa/dialogue_understanding/commands/handle_digressions_command.py +1 -7
  43. rasa/dialogue_understanding/commands/human_handoff_command.py +20 -2
  44. rasa/dialogue_understanding/commands/knowledge_answer_command.py +20 -2
  45. rasa/dialogue_understanding/commands/prompt_command.py +94 -0
  46. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +20 -2
  47. rasa/dialogue_understanding/commands/set_slot_command.py +24 -2
  48. rasa/dialogue_understanding/commands/skip_question_command.py +20 -2
  49. rasa/dialogue_understanding/commands/start_flow_command.py +22 -2
  50. rasa/dialogue_understanding/commands/utils.py +71 -4
  51. rasa/dialogue_understanding/generator/__init__.py +2 -0
  52. rasa/dialogue_understanding/generator/command_parser.py +15 -12
  53. rasa/dialogue_understanding/generator/constants.py +3 -0
  54. rasa/dialogue_understanding/generator/llm_based_command_generator.py +12 -5
  55. rasa/dialogue_understanding/generator/llm_command_generator.py +5 -3
  56. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +17 -3
  57. rasa/dialogue_understanding/generator/prompt_templates/__init__.py +0 -0
  58. rasa/dialogue_understanding/generator/{single_step → prompt_templates}/command_prompt_template.jinja2 +2 -0
  59. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +77 -0
  60. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_default.jinja2 +68 -0
  61. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +84 -0
  62. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +522 -0
  63. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +12 -310
  64. rasa/dialogue_understanding/patterns/collect_information.py +1 -1
  65. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +16 -0
  66. rasa/dialogue_understanding/patterns/validate_slot.py +65 -0
  67. rasa/dialogue_understanding/processor/command_processor.py +39 -0
  68. rasa/dialogue_understanding/stack/utils.py +38 -0
  69. rasa/dialogue_understanding_test/du_test_case.py +58 -18
  70. rasa/dialogue_understanding_test/du_test_result.py +14 -10
  71. rasa/dialogue_understanding_test/io.py +14 -0
  72. rasa/e2e_test/assertions.py +6 -8
  73. rasa/e2e_test/llm_judge_prompts/answer_relevance_prompt_template.jinja2 +5 -1
  74. rasa/e2e_test/llm_judge_prompts/groundedness_prompt_template.jinja2 +4 -0
  75. rasa/e2e_test/utils/io.py +0 -37
  76. rasa/engine/graph.py +1 -0
  77. rasa/engine/language.py +140 -0
  78. rasa/engine/recipes/config_files/default_config.yml +4 -0
  79. rasa/engine/recipes/default_recipe.py +2 -0
  80. rasa/engine/recipes/graph_recipe.py +2 -0
  81. rasa/engine/storage/local_model_storage.py +1 -0
  82. rasa/engine/storage/storage.py +4 -1
  83. rasa/llm_fine_tuning/conversations.py +1 -1
  84. rasa/model_manager/runner_service.py +7 -4
  85. rasa/model_manager/socket_bridge.py +7 -6
  86. rasa/shared/constants.py +15 -13
  87. rasa/shared/core/constants.py +2 -0
  88. rasa/shared/core/flows/constants.py +11 -0
  89. rasa/shared/core/flows/flow.py +83 -19
  90. rasa/shared/core/flows/flows_yaml_schema.json +31 -3
  91. rasa/shared/core/flows/steps/collect.py +1 -36
  92. rasa/shared/core/flows/utils.py +28 -4
  93. rasa/shared/core/flows/validation.py +1 -1
  94. rasa/shared/core/slot_mappings.py +208 -5
  95. rasa/shared/core/slots.py +137 -1
  96. rasa/shared/core/trackers.py +74 -1
  97. rasa/shared/importers/importer.py +50 -2
  98. rasa/shared/nlu/training_data/schemas/responses.yml +19 -12
  99. rasa/shared/providers/_configs/azure_entra_id_config.py +541 -0
  100. rasa/shared/providers/_configs/azure_openai_client_config.py +138 -3
  101. rasa/shared/providers/_configs/client_config.py +3 -1
  102. rasa/shared/providers/_configs/default_litellm_client_config.py +3 -1
  103. rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +3 -1
  104. rasa/shared/providers/_configs/litellm_router_client_config.py +3 -1
  105. rasa/shared/providers/_configs/model_group_config.py +4 -2
  106. rasa/shared/providers/_configs/oauth_config.py +33 -0
  107. rasa/shared/providers/_configs/openai_client_config.py +3 -1
  108. rasa/shared/providers/_configs/rasa_llm_client_config.py +3 -1
  109. rasa/shared/providers/_configs/self_hosted_llm_client_config.py +3 -1
  110. rasa/shared/providers/constants.py +6 -0
  111. rasa/shared/providers/embedding/azure_openai_embedding_client.py +28 -3
  112. rasa/shared/providers/embedding/litellm_router_embedding_client.py +3 -1
  113. rasa/shared/providers/llm/_base_litellm_client.py +42 -17
  114. rasa/shared/providers/llm/azure_openai_llm_client.py +81 -25
  115. rasa/shared/providers/llm/default_litellm_llm_client.py +3 -1
  116. rasa/shared/providers/llm/litellm_router_llm_client.py +29 -8
  117. rasa/shared/providers/llm/llm_client.py +23 -7
  118. rasa/shared/providers/llm/openai_llm_client.py +9 -3
  119. rasa/shared/providers/llm/rasa_llm_client.py +11 -2
  120. rasa/shared/providers/llm/self_hosted_llm_client.py +30 -11
  121. rasa/shared/providers/router/_base_litellm_router_client.py +3 -1
  122. rasa/shared/providers/router/router_client.py +3 -1
  123. rasa/shared/utils/constants.py +3 -0
  124. rasa/shared/utils/llm.py +33 -7
  125. rasa/shared/utils/pykwalify_extensions.py +24 -0
  126. rasa/shared/utils/schemas/domain.yml +26 -0
  127. rasa/telemetry.py +2 -1
  128. rasa/tracing/config.py +2 -0
  129. rasa/tracing/constants.py +12 -0
  130. rasa/tracing/instrumentation/instrumentation.py +36 -0
  131. rasa/tracing/instrumentation/metrics.py +41 -0
  132. rasa/tracing/metric_instrument_provider.py +40 -0
  133. rasa/validator.py +372 -7
  134. rasa/version.py +1 -1
  135. {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc2.dist-info}/METADATA +13 -14
  136. {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc2.dist-info}/RECORD +139 -124
  137. {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc2.dist-info}/NOTICE +0 -0
  138. {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc2.dist-info}/WHEEL +0 -0
  139. {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc2.dist-info}/entry_points.txt +0 -0
README.md CHANGED
@@ -2,15 +2,14 @@
2
2
 
3
3
  <div align="center">
4
4
 
5
- [![Build Status](https://github.com/RasaHQ/rasa-private/workflows/Continuous%20Integration/badge.svg)](https://github.com/RasaHQ/rasa-private/actions)
6
5
  [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=RasaHQ_rasa&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=RasaHQ_rasa)
7
- [![Documentation Status](https://img.shields.io/badge/docs-stable-brightgreen.svg)](https://rasa.com/docs/rasa-pro/)
6
+ [![Documentation Status](https://img.shields.io/badge/docs-stable-brightgreen.svg)](https://rasa.com/docs/docs/pro/intro)
7
+ ![Python version support](https://img.shields.io/pypi/pyversions/rasa-pro)
8
8
 
9
9
  </div>
10
10
 
11
11
  <hr />
12
12
 
13
-
14
13
  Rasa Pro is a framework for building scalable, dynamic conversational AI assistants that integrate large language models (LLMs) to enable more contextually aware and agentic interactions. Whether you’re new to conversational AI or an experienced developer, Rasa Pro offers enhanced flexibility, control, and performance for mission-critical applications.
15
14
 
16
15
  Building on the foundation of Rasa Open Source, Rasa Pro adds advanced features like CALM (Conversational AI with Language Models) and Dialogue Understanding (DU), which enable developers to shift from traditional intent-driven systems to LLM-based agents. This allows for more robust, responsive interactions that adhere strictly to business logic, while reducing risks like prompt injection and minimizing hallucinations.
@@ -23,19 +22,17 @@ Building on the foundation of Rasa Open Source, Rasa Pro adds advanced features
23
22
  - **Robustness and Control:** Maintain strict adherence to business logic, preventing unwanted behaviors like prompt injection and hallucinations, leading to more reliable responses and secure interactions.
24
23
  - **Built-in Security:** Safeguard sensitive data, control access, and ensure secure deployment, essential for production environments that demand high levels of security and compliance.
25
24
 
25
+ A [free developer license](https://rasa.com/docs/pro/intro/#who-rasa-pro-is-for) is available so you can explore and get to know Rasa Pro. It allows you to take your assistant live in production a limited capacity. A paid license is required for larger-scale production use, but all code is visible and can be customized as needed.
26
26
 
27
+ To get started right now, you can
27
28
 
28
- A [free developer license](https://rasa.com/docs/rasa-pro/developer-edition/) is available so you can explore and get to know Rasa Pro. For small production deployments, the Extended Developer License allows you to take your assistant live in a limited capacity. A paid license is required for larger-scale production use, but all code is visible and can be customized as needed.
29
-
30
- To get started right now, you can
31
-
32
- `pip install rasa-pro`
29
+ `pip install rasa-pro`
33
30
 
34
- Check out our
31
+ Check out our
35
32
 
36
- - [Rasa-pro Quickstart](https://rasa.com/docs/rasa-pro/installation/quickstart/),
37
- - [Conversational AI with Language Models (CALM) conceptual rundown](https://rasa.com/docs/rasa-pro/calm/),
38
- - [Rasa Pro / CALM tutorial](https://rasa.com/docs/rasa-pro/tutorial), and
39
- - [Rasa pro changelog](https://rasa.com/docs/rasa/rasa-pro-changelog/)
33
+ - [Rasa-pro Quickstart](https://rasa.com/docs/learn/quickstart/pro),
34
+ - [Conversational AI with Language Models (CALM) conceptual rundown](https://rasa.com/docs/learn/concepts/calm),
35
+ - [Rasa Pro / CALM tutorial](https://rasa.com/docs/pro/tutorial), and
36
+ - [Rasa pro changelog](https://rasa.com/docs/reference/changelogs/rasa-pro-changelog)
40
37
 
41
38
  for more. Also feel free to reach out to us on the [Rasa forum](https://forum.rasa.com/).
@@ -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(
@@ -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
@@ -7,8 +7,7 @@ slots:
7
7
  contacts_list:
8
8
  type: text
9
9
  mappings:
10
- - type: custom
11
- action: list_contacts
10
+ - type: controlled
12
11
 
13
12
  responses:
14
13
  utter_no_contacts:
@@ -7,8 +7,7 @@ slots:
7
7
  remove_contact_name:
8
8
  type: text
9
9
  mappings:
10
- - type: custom
11
- action: remove_contact
10
+ - type: controlled
12
11
  remove_contact_handle:
13
12
  type: text
14
13
  mappings:
@@ -4,7 +4,4 @@ slots:
4
4
  return_value:
5
5
  type: any
6
6
  mappings:
7
- - type: custom
8
- action: add_contact
9
- - type: custom
10
- action: remove_contact
7
+ - type: controlled
@@ -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/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
69
  REQUESTED_SLOT,
77
70
  USER_INTENT_OUT_OF_SCOPE,
78
71
  SetSlotExtractor,
79
- SlotMappingType,
80
72
  )
81
73
  from rasa.shared.core.domain import Domain
82
74
  from rasa.shared.core.events import (
@@ -92,26 +84,23 @@ 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:
111
101
  from rasa.core.channels.channel import OutputChannel
112
102
  from rasa.core.nlg import NaturalLanguageGenerator
113
103
  from rasa.shared.core.events import IntentPrediction
114
- from rasa.shared.core.slot_mappings import SlotMapping
115
104
 
116
105
  logger = logging.getLogger(__name__)
117
106
 
@@ -268,25 +257,36 @@ def action_for_name_or_text(
268
257
  return RemoteAction(action_name_or_text, action_endpoint)
269
258
 
270
259
 
271
- def create_bot_utterance(message: Dict[Text, Any]) -> BotUttered:
272
- """Create BotUttered event from message."""
273
- bot_message = BotUttered(
274
- text=message.pop(TEXT, None),
275
- data={
276
- ELEMENTS: message.pop(ELEMENTS, None),
277
- QUICK_REPLIES: message.pop(QUICK_REPLIES, None),
278
- BUTTONS: message.pop(BUTTONS, None),
279
- # for legacy / compatibility reasons we need to set the image
280
- # to be the attachment if there is no other attachment (the
281
- # `.get` is intentional - no `pop` as we still need the image`
282
- # property to set it in the following line)
283
- ATTACHMENT: message.pop(ATTACHMENT, None) or message.get(IMAGE, None),
284
- IMAGE: message.pop(IMAGE, None),
285
- CUSTOM: message.pop(CUSTOM, None),
286
- },
287
- 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,
270
+ )
271
+
272
+ buttons = get_translated_buttons(
273
+ buttons=message_copy.pop(BUTTONS, None), language=language
288
274
  )
289
- return bot_message
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)
290
290
 
291
291
 
292
292
  class Action:
@@ -399,7 +399,7 @@ class ActionBotResponse(Action):
399
399
  message = add_bot_utterance_metadata(
400
400
  message, self.utter_action, nlg, domain, tracker
401
401
  )
402
- return [create_bot_utterance(message)]
402
+ return [create_bot_utterance(message, tracker.current_language)]
403
403
 
404
404
  def name(self) -> Text:
405
405
  """Returns action name."""
@@ -433,7 +433,7 @@ class ActionEndToEndResponse(Action):
433
433
  ) -> List[Event]:
434
434
  """Runs action (see parent class for full docstring)."""
435
435
  message = {"text": self.action_text}
436
- return [create_bot_utterance(message)]
436
+ return [create_bot_utterance(message, tracker.current_language)]
437
437
 
438
438
  def event_for_successful_execution(
439
439
  self,
@@ -917,7 +917,7 @@ class RemoteAction(Action):
917
917
  # Avoid overwriting `draft` values with empty values
918
918
  response = {k: v for k, v in response.items() if v}
919
919
  draft.update(response)
920
- bot_messages.append(create_bot_utterance(draft))
920
+ bot_messages.append(create_bot_utterance(draft, tracker.current_language))
921
921
 
922
922
  return bot_messages
923
923
 
@@ -1125,7 +1125,7 @@ class ActionDefaultAskAffirmation(Action):
1125
1125
  "utter_action": self.name(),
1126
1126
  }
1127
1127
 
1128
- return [create_bot_utterance(message)]
1128
+ return [create_bot_utterance(message, tracker.current_language)]
1129
1129
 
1130
1130
 
1131
1131
  class ActionDefaultAskRephrase(ActionBotResponse):
@@ -1158,7 +1158,7 @@ class ActionSendText(Action):
1158
1158
  fallback = {"text": ""}
1159
1159
  metadata_copy = copy.deepcopy(metadata) if metadata else {}
1160
1160
  message = metadata_copy.get("message", fallback)
1161
- return [create_bot_utterance(message)]
1161
+ return [create_bot_utterance(message, tracker.current_language)]
1162
1162
 
1163
1163
 
1164
1164
  class ActionExtractSlots(Action):
@@ -1179,113 +1179,6 @@ class ActionExtractSlots(Action):
1179
1179
  """Returns action_extract_slots name."""
1180
1180
  return ACTION_EXTRACT_SLOTS
1181
1181
 
1182
- async def _run_custom_action(
1183
- self,
1184
- custom_action: Text,
1185
- output_channel: "OutputChannel",
1186
- nlg: "NaturalLanguageGenerator",
1187
- tracker: "DialogueStateTracker",
1188
- domain: "Domain",
1189
- ) -> List[Event]:
1190
- slot_events: List[Event] = []
1191
- remote_action = RemoteAction(custom_action, self._action_endpoint)
1192
- disallowed_types = set()
1193
-
1194
- try:
1195
- custom_events = await remote_action.run(
1196
- output_channel, nlg, tracker, domain
1197
- )
1198
- for event in custom_events:
1199
- if isinstance(event, SlotSet):
1200
- slot_events.append(event)
1201
- elif isinstance(event, BotUttered):
1202
- slot_events.append(event)
1203
- else:
1204
- disallowed_types.add(event.type_name)
1205
- except (RasaException, ClientResponseError) as e:
1206
- logger.warning(
1207
- f"Failed to execute custom action '{custom_action}' "
1208
- f"as a result of error '{e!s}'. The default action "
1209
- f"'{self.name()}' failed to fill slots with custom "
1210
- f"mappings."
1211
- )
1212
-
1213
- for type_name in disallowed_types:
1214
- logger.info(
1215
- f"Running custom action '{custom_action}' has resulted "
1216
- f"in an event of type '{type_name}'. This is "
1217
- f"disallowed and the tracker will not be "
1218
- f"updated with this event."
1219
- )
1220
-
1221
- return slot_events
1222
-
1223
- async def _execute_custom_action(
1224
- self,
1225
- mapping: "SlotMapping",
1226
- executed_custom_actions: Set[Text],
1227
- output_channel: "OutputChannel",
1228
- nlg: "NaturalLanguageGenerator",
1229
- tracker: "DialogueStateTracker",
1230
- domain: "Domain",
1231
- calm_custom_action_names: Optional[Set[str]] = None,
1232
- ) -> Tuple[List[Event], Set[Text]]:
1233
- custom_action = mapping.run_action_every_turn
1234
-
1235
- if not custom_action or custom_action in executed_custom_actions:
1236
- return [], executed_custom_actions
1237
-
1238
- if (
1239
- calm_custom_action_names is not None
1240
- and custom_action in calm_custom_action_names
1241
- ):
1242
- return [], executed_custom_actions
1243
-
1244
- slot_events = await self._run_custom_action(
1245
- custom_action, output_channel, nlg, tracker, domain
1246
- )
1247
-
1248
- executed_custom_actions.add(custom_action)
1249
-
1250
- return slot_events, executed_custom_actions
1251
-
1252
- async def _execute_validation_action(
1253
- self,
1254
- extraction_events: List[Event],
1255
- output_channel: "OutputChannel",
1256
- nlg: "NaturalLanguageGenerator",
1257
- tracker: "DialogueStateTracker",
1258
- domain: "Domain",
1259
- ) -> List[Event]:
1260
- slot_events: List[SlotSet] = [
1261
- event for event in extraction_events if isinstance(event, SlotSet)
1262
- ]
1263
-
1264
- slot_candidates = "\n".join([e.key for e in slot_events])
1265
- logger.debug(f"Validating extracted slots: {slot_candidates}")
1266
-
1267
- if ACTION_VALIDATE_SLOT_MAPPINGS not in domain.user_actions:
1268
- return cast(List[Event], slot_events)
1269
-
1270
- _tracker = DialogueStateTracker.from_events(
1271
- tracker.sender_id,
1272
- tracker.events_after_latest_restart() + cast(List[Event], slot_events),
1273
- slots=domain.slots,
1274
- )
1275
- validate_events = await self._run_custom_action(
1276
- ACTION_VALIDATE_SLOT_MAPPINGS, output_channel, nlg, _tracker, domain
1277
- )
1278
- validated_slot_names = [
1279
- event.key for event in validate_events if isinstance(event, SlotSet)
1280
- ]
1281
-
1282
- # If the custom action doesn't return a SlotSet event for an extracted slot
1283
- # candidate we assume that it was valid. The custom action has to return a
1284
- # SlotSet(slot_name, None) event to mark a Slot as invalid.
1285
- return validate_events + [
1286
- event for event in slot_events if event.key not in validated_slot_names
1287
- ]
1288
-
1289
1182
  async def run(
1290
1183
  self,
1291
1184
  output_channel: "OutputChannel",
@@ -1296,59 +1189,59 @@ class ActionExtractSlots(Action):
1296
1189
  ) -> List[Event]:
1297
1190
  """Runs action. Please see parent class for the full docstring."""
1298
1191
  slot_events: List[Event] = []
1299
- executed_custom_actions: Set[Text] = set()
1300
-
1301
1192
  user_slots = [
1302
1193
  slot
1303
1194
  for slot in domain.slots
1304
1195
  if slot.name not in DEFAULT_SLOT_NAMES | KNOWLEDGE_BASE_SLOT_NAMES
1305
1196
  ]
1306
1197
 
1307
- calm_slot_names = set()
1308
- calm_custom_action_names = None
1309
- flows = None
1310
-
1311
- if metadata is not None:
1312
- flows = metadata.get("all_flows")
1313
-
1314
- if flows is not None:
1315
- flows = FlowsList.from_json(flows)
1316
- calm_slot_names = flows.available_slot_names()
1317
- calm_custom_action_names = flows.available_custom_actions()
1318
-
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
+ )
1319
1205
  slot_filling_manager = SlotFillingManager(
1320
- domain, tracker, action_endpoint=self._action_endpoint
1206
+ domain,
1207
+ tracker,
1208
+ action_endpoint=self._action_endpoint,
1321
1209
  )
1322
1210
 
1323
1211
  for slot in user_slots:
1324
1212
  # allows the action to set slots that are shared between
1325
1213
  # the NLU-based system and CALM system in a coexistence bot
1326
- if slot.name in calm_slot_names and not slot.shared_for_coexistence:
1327
- continue
1328
-
1329
- slot_value, is_extracted = extract_slot_value(slot, slot_filling_manager)
1330
- if is_extracted:
1331
- slot_events.append(SlotSet(slot.name, slot_value))
1332
-
1333
- for mapping in slot.mappings:
1334
- should_fill_controlled_slot = mapping.type == SlotMappingType.CONTROLLED
1335
-
1336
- if should_fill_controlled_slot:
1337
- (
1338
- custom_evts,
1339
- executed_custom_actions,
1340
- ) = await self._execute_custom_action(
1341
- mapping,
1342
- executed_custom_actions,
1343
- output_channel,
1344
- nlg,
1345
- tracker,
1346
- domain,
1347
- 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,
1348
1220
  )
1349
- slot_events.extend(custom_evts)
1221
+ )
1222
+ if not should_fill_slot_in_coexistence:
1223
+ continue
1350
1224
 
1351
- validated_events = await self._execute_validation_action(
1352
- slot_events, output_channel, nlg, tracker, domain
1353
- )
1354
- 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