rasa-pro 3.12.0.dev13__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 (128) hide show
  1. rasa/anonymization/anonymization_rule_executor.py +16 -10
  2. rasa/cli/data.py +16 -0
  3. rasa/cli/project_templates/calm/config.yml +2 -2
  4. rasa/cli/project_templates/calm/endpoints.yml +2 -2
  5. rasa/cli/utils.py +12 -0
  6. rasa/core/actions/action.py +84 -191
  7. rasa/core/actions/action_run_slot_rejections.py +16 -4
  8. rasa/core/channels/__init__.py +2 -0
  9. rasa/core/channels/studio_chat.py +19 -0
  10. rasa/core/channels/telegram.py +42 -24
  11. rasa/core/channels/voice_ready/utils.py +1 -1
  12. rasa/core/channels/voice_stream/asr/asr_engine.py +10 -4
  13. rasa/core/channels/voice_stream/asr/azure.py +14 -1
  14. rasa/core/channels/voice_stream/asr/deepgram.py +20 -4
  15. rasa/core/channels/voice_stream/audiocodes.py +264 -0
  16. rasa/core/channels/voice_stream/browser_audio.py +4 -1
  17. rasa/core/channels/voice_stream/call_state.py +3 -0
  18. rasa/core/channels/voice_stream/genesys.py +6 -2
  19. rasa/core/channels/voice_stream/tts/azure.py +9 -1
  20. rasa/core/channels/voice_stream/tts/cartesia.py +14 -8
  21. rasa/core/channels/voice_stream/voice_channel.py +23 -2
  22. rasa/core/constants.py +2 -0
  23. rasa/core/nlg/contextual_response_rephraser.py +18 -1
  24. rasa/core/nlg/generator.py +83 -15
  25. rasa/core/nlg/response.py +6 -3
  26. rasa/core/nlg/translate.py +55 -0
  27. rasa/core/policies/enterprise_search_prompt_with_citation_template.jinja2 +1 -1
  28. rasa/core/policies/flows/flow_executor.py +12 -5
  29. rasa/core/processor.py +72 -9
  30. rasa/dialogue_understanding/commands/can_not_handle_command.py +20 -2
  31. rasa/dialogue_understanding/commands/cancel_flow_command.py +24 -6
  32. rasa/dialogue_understanding/commands/change_flow_command.py +20 -2
  33. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +20 -2
  34. rasa/dialogue_understanding/commands/clarify_command.py +29 -3
  35. rasa/dialogue_understanding/commands/command.py +1 -16
  36. rasa/dialogue_understanding/commands/command_syntax_manager.py +55 -0
  37. rasa/dialogue_understanding/commands/human_handoff_command.py +20 -2
  38. rasa/dialogue_understanding/commands/knowledge_answer_command.py +20 -2
  39. rasa/dialogue_understanding/commands/prompt_command.py +94 -0
  40. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +20 -2
  41. rasa/dialogue_understanding/commands/set_slot_command.py +24 -2
  42. rasa/dialogue_understanding/commands/skip_question_command.py +20 -2
  43. rasa/dialogue_understanding/commands/start_flow_command.py +20 -2
  44. rasa/dialogue_understanding/commands/utils.py +98 -4
  45. rasa/dialogue_understanding/generator/__init__.py +2 -0
  46. rasa/dialogue_understanding/generator/command_parser.py +15 -12
  47. rasa/dialogue_understanding/generator/constants.py +3 -0
  48. rasa/dialogue_understanding/generator/llm_based_command_generator.py +12 -5
  49. rasa/dialogue_understanding/generator/llm_command_generator.py +5 -3
  50. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +16 -2
  51. rasa/dialogue_understanding/generator/prompt_templates/__init__.py +0 -0
  52. rasa/dialogue_understanding/generator/{single_step → prompt_templates}/command_prompt_template.jinja2 +2 -0
  53. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +77 -0
  54. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_default.jinja2 +68 -0
  55. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +84 -0
  56. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +460 -0
  57. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +12 -310
  58. rasa/dialogue_understanding/patterns/collect_information.py +1 -1
  59. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +16 -0
  60. rasa/dialogue_understanding/patterns/validate_slot.py +65 -0
  61. rasa/dialogue_understanding/processor/command_processor.py +39 -0
  62. rasa/dialogue_understanding_test/du_test_case.py +28 -8
  63. rasa/dialogue_understanding_test/du_test_result.py +13 -9
  64. rasa/dialogue_understanding_test/io.py +14 -0
  65. rasa/e2e_test/utils/io.py +0 -37
  66. rasa/engine/graph.py +1 -0
  67. rasa/engine/language.py +140 -0
  68. rasa/engine/recipes/config_files/default_config.yml +4 -0
  69. rasa/engine/recipes/default_recipe.py +2 -0
  70. rasa/engine/recipes/graph_recipe.py +2 -0
  71. rasa/engine/storage/local_model_storage.py +1 -0
  72. rasa/engine/storage/storage.py +4 -1
  73. rasa/model_manager/runner_service.py +7 -4
  74. rasa/model_manager/socket_bridge.py +7 -6
  75. rasa/shared/constants.py +15 -13
  76. rasa/shared/core/constants.py +2 -0
  77. rasa/shared/core/flows/constants.py +11 -0
  78. rasa/shared/core/flows/flow.py +83 -19
  79. rasa/shared/core/flows/flows_yaml_schema.json +31 -3
  80. rasa/shared/core/flows/steps/collect.py +1 -36
  81. rasa/shared/core/flows/utils.py +28 -4
  82. rasa/shared/core/flows/validation.py +1 -1
  83. rasa/shared/core/slot_mappings.py +208 -5
  84. rasa/shared/core/slots.py +131 -1
  85. rasa/shared/core/trackers.py +74 -1
  86. rasa/shared/importers/importer.py +50 -2
  87. rasa/shared/nlu/training_data/schemas/responses.yml +19 -12
  88. rasa/shared/providers/_configs/azure_entra_id_config.py +541 -0
  89. rasa/shared/providers/_configs/azure_openai_client_config.py +138 -3
  90. rasa/shared/providers/_configs/client_config.py +3 -1
  91. rasa/shared/providers/_configs/default_litellm_client_config.py +3 -1
  92. rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +3 -1
  93. rasa/shared/providers/_configs/litellm_router_client_config.py +3 -1
  94. rasa/shared/providers/_configs/model_group_config.py +4 -2
  95. rasa/shared/providers/_configs/oauth_config.py +33 -0
  96. rasa/shared/providers/_configs/openai_client_config.py +3 -1
  97. rasa/shared/providers/_configs/rasa_llm_client_config.py +3 -1
  98. rasa/shared/providers/_configs/self_hosted_llm_client_config.py +3 -1
  99. rasa/shared/providers/constants.py +6 -0
  100. rasa/shared/providers/embedding/azure_openai_embedding_client.py +28 -3
  101. rasa/shared/providers/embedding/litellm_router_embedding_client.py +3 -1
  102. rasa/shared/providers/llm/_base_litellm_client.py +42 -17
  103. rasa/shared/providers/llm/azure_openai_llm_client.py +81 -25
  104. rasa/shared/providers/llm/default_litellm_llm_client.py +3 -1
  105. rasa/shared/providers/llm/litellm_router_llm_client.py +29 -8
  106. rasa/shared/providers/llm/llm_client.py +23 -7
  107. rasa/shared/providers/llm/openai_llm_client.py +9 -3
  108. rasa/shared/providers/llm/rasa_llm_client.py +11 -2
  109. rasa/shared/providers/llm/self_hosted_llm_client.py +30 -11
  110. rasa/shared/providers/router/_base_litellm_router_client.py +3 -1
  111. rasa/shared/providers/router/router_client.py +3 -1
  112. rasa/shared/utils/constants.py +3 -0
  113. rasa/shared/utils/llm.py +30 -7
  114. rasa/shared/utils/pykwalify_extensions.py +24 -0
  115. rasa/shared/utils/schemas/domain.yml +26 -0
  116. rasa/telemetry.py +2 -1
  117. rasa/tracing/config.py +2 -0
  118. rasa/tracing/constants.py +12 -0
  119. rasa/tracing/instrumentation/instrumentation.py +36 -0
  120. rasa/tracing/instrumentation/metrics.py +41 -0
  121. rasa/tracing/metric_instrument_provider.py +40 -0
  122. rasa/validator.py +372 -7
  123. rasa/version.py +1 -1
  124. {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc1.dist-info}/METADATA +2 -1
  125. {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc1.dist-info}/RECORD +128 -113
  126. {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc1.dist-info}/NOTICE +0 -0
  127. {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc1.dist-info}/WHEEL +0 -0
  128. {rasa_pro-3.12.0.dev13.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(
@@ -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/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
@@ -9,14 +9,17 @@ from rasa.core.utils import add_bot_utterance_metadata
9
9
  from rasa.dialogue_understanding.patterns.collect_information import (
10
10
  CollectInformationPatternFlowStackFrame,
11
11
  )
12
+ from rasa.dialogue_understanding.patterns.validate_slot import (
13
+ ValidateSlotPatternFlowStackFrame,
14
+ )
12
15
  from rasa.shared.core.constants import ACTION_RUN_SLOT_REJECTIONS_NAME
13
16
  from rasa.shared.core.events import Event, SlotSet
14
- from rasa.shared.core.flows.steps.collect import SlotRejection
15
17
  from rasa.shared.core.slots import (
16
18
  BooleanSlot,
17
19
  CategoricalSlot,
18
20
  FloatSlot,
19
21
  Slot,
22
+ SlotRejection,
20
23
  )
21
24
 
22
25
  if TYPE_CHECKING:
@@ -138,10 +141,19 @@ class ActionRunSlotRejections(Action):
138
141
  """Run the predicate checks."""
139
142
  utterance = None
140
143
  top_frame = tracker.stack.top()
141
- if not isinstance(top_frame, CollectInformationPatternFlowStackFrame):
144
+ if not isinstance(
145
+ top_frame,
146
+ (
147
+ CollectInformationPatternFlowStackFrame,
148
+ ValidateSlotPatternFlowStackFrame,
149
+ ),
150
+ ):
142
151
  return []
152
+ elif isinstance(top_frame, CollectInformationPatternFlowStackFrame):
153
+ slot_name = top_frame.collect
154
+ elif isinstance(top_frame, ValidateSlotPatternFlowStackFrame):
155
+ slot_name = top_frame.validate
143
156
 
144
- slot_name = top_frame.collect
145
157
  slot_instance = tracker.slots.get(slot_name)
146
158
  if slot_instance and not slot_instance.has_been_set:
147
159
  # this is the first time the assistant asks for the slot value,
@@ -205,6 +217,6 @@ class ActionRunSlotRejections(Action):
205
217
  message = add_bot_utterance_metadata(
206
218
  message, utterance, nlg, domain, tracker
207
219
  )
208
- events.append(create_bot_utterance(message))
220
+ events.append(create_bot_utterance(message, tracker.current_language))
209
221
 
210
222
  return events
@@ -34,6 +34,7 @@ from rasa.core.channels.voice_stream.twilio_media_streams import (
34
34
  )
35
35
  from rasa.core.channels.voice_stream.genesys import GenesysInputChannel
36
36
  from rasa.core.channels.studio_chat import StudioChatInput
37
+ from rasa.core.channels.voice_stream.audiocodes import AudiocodesVoiceInputChannel
37
38
 
38
39
  input_channel_classes: List[Type[InputChannel]] = [
39
40
  CmdlineInput,
@@ -58,6 +59,7 @@ input_channel_classes: List[Type[InputChannel]] = [
58
59
  BrowserAudioInputChannel,
59
60
  GenesysInputChannel,
60
61
  StudioChatInput,
62
+ AudiocodesVoiceInputChannel,
61
63
  ]
62
64
 
63
65
  # Mapping from an input channel name to its class to allow name based lookup.
@@ -18,6 +18,8 @@ from sanic import Sanic
18
18
  from rasa.core.channels.socketio import SocketBlueprint, SocketIOInput
19
19
  from rasa.hooks import hookimpl
20
20
  from rasa.plugin import plugin_manager
21
+ from rasa.shared.core.constants import ACTION_LISTEN_NAME
22
+ from rasa.shared.core.events import ActionExecuted
21
23
  from rasa.shared.core.trackers import EventVerbosity
22
24
 
23
25
  if TYPE_CHECKING:
@@ -43,6 +45,15 @@ def tracker_as_dump(tracker: "DialogueStateTracker") -> str:
43
45
  return json.dumps(state)
44
46
 
45
47
 
48
+ def does_need_action_prediction(tracker: "DialogueStateTracker") -> bool:
49
+ """Check if the tracker needs an action prediction."""
50
+ return (
51
+ len(tracker.events) == 0
52
+ or not isinstance(tracker.events[-1], ActionExecuted)
53
+ or tracker.events[-1].action_name != ACTION_LISTEN_NAME
54
+ )
55
+
56
+
46
57
  class StudioTrackerUpdatePlugin:
47
58
  """Plugin for publishing tracker updates a socketio channel."""
48
59
 
@@ -169,6 +180,14 @@ class StudioChatInput(SocketIOInput):
169
180
  # will override an existing tracker with the same id!
170
181
  await self.agent.tracker_store.save(tracker)
171
182
 
183
+ processor = self.agent.processor
184
+ if processor and does_need_action_prediction(tracker):
185
+ output_channel = self.get_output_channel()
186
+
187
+ await processor._run_prediction_loop(output_channel, tracker)
188
+ await processor.run_anonymization_pipeline(tracker)
189
+ await self.agent.tracker_store.save(tracker)
190
+
172
191
  await self.on_tracker_updated(tracker)
173
192
 
174
193
  def blueprint(