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
@@ -24,6 +24,7 @@ structlogger = structlog.get_logger()
24
24
  class CartesiaTTSConfig(TTSEngineConfig):
25
25
  model_id: Optional[str] = None
26
26
  version: Optional[str] = None
27
+ endpoint: Optional[str] = None
27
28
 
28
29
 
29
30
  class CartesiaTTS(TTSEngine[CartesiaTTSConfig]):
@@ -38,11 +39,6 @@ class CartesiaTTS(TTSEngine[CartesiaTTSConfig]):
38
39
  if self.__class__.session is None or self.__class__.session.closed:
39
40
  self.__class__.session = aiohttp.ClientSession(timeout=timeout)
40
41
 
41
- @staticmethod
42
- def get_tts_endpoint() -> str:
43
- """Create the endpoint string for cartesia."""
44
- return "https://api.cartesia.ai/tts/sse"
45
-
46
42
  @staticmethod
47
43
  def get_request_body(text: str, config: CartesiaTTSConfig) -> Dict:
48
44
  """Create the request body for cartesia."""
@@ -79,7 +75,7 @@ class CartesiaTTS(TTSEngine[CartesiaTTSConfig]):
79
75
  config = self.config.merge(config)
80
76
  payload = self.get_request_body(text, config)
81
77
  headers = self.get_request_headers(config)
82
- url = self.get_tts_endpoint()
78
+ url = self.config.endpoint
83
79
  if self.session is None:
84
80
  raise ConnectionException("Client session is not initialized")
85
81
  try:
@@ -101,13 +97,22 @@ class CartesiaTTS(TTSEngine[CartesiaTTSConfig]):
101
97
  channel_bytes
102
98
  )
103
99
  return
100
+ elif response.status == 401:
101
+ structlogger.error(
102
+ "cartesia.synthesize.rest.unauthorized",
103
+ status_code=response.status,
104
+ )
105
+ raise TTSError(
106
+ "Unauthorized. Please make sure you have the correct API key."
107
+ )
104
108
  else:
109
+ response_text = await response.text()
105
110
  structlogger.error(
106
111
  "cartesia.synthesize.rest.failed",
107
112
  status_code=response.status,
108
- msg=response.text(),
113
+ msg=response_text,
109
114
  )
110
- raise TTSError(f"TTS failed: {response.text()}")
115
+ raise TTSError(f"TTS failed: {response_text}")
111
116
  except ClientConnectorError as e:
112
117
  raise TTSError(e)
113
118
  except TimeoutError as e:
@@ -125,6 +130,7 @@ class CartesiaTTS(TTSEngine[CartesiaTTSConfig]):
125
130
  timeout=10,
126
131
  model_id="sonic-english",
127
132
  version="2024-06-10",
133
+ endpoint="https://api.cartesia.ai/tts/sse",
128
134
  )
129
135
 
130
136
  @classmethod
@@ -148,6 +148,19 @@ class VoiceOutputChannel(OutputChannel):
148
148
  await self.voice_websocket.send(marker_message)
149
149
  self.latest_message_id = mark_id
150
150
 
151
+ async def send_start_marker(self, recipient_id: str) -> None:
152
+ """Send a marker message before the first audio chunk."""
153
+ # Default implementation uses the generic marker message
154
+ await self.send_marker_message(recipient_id)
155
+
156
+ async def send_intermediate_marker(self, recipient_id: str) -> None:
157
+ """Send a marker message during audio streaming."""
158
+ await self.send_marker_message(recipient_id)
159
+
160
+ async def send_end_marker(self, recipient_id: str) -> None:
161
+ """Send a marker message after the last audio chunk."""
162
+ await self.send_marker_message(recipient_id)
163
+
151
164
  def update_silence_timeout(self) -> None:
152
165
  """Updates the silence timeout for the session."""
153
166
  if self.tracker_state:
@@ -173,6 +186,13 @@ class VoiceOutputChannel(OutputChannel):
173
186
  cached_audio_bytes = self.tts_cache.get(text)
174
187
  collected_audio_bytes = RasaAudioBytes(b"")
175
188
  seconds_marker = -1
189
+
190
+ # Send start marker before first chunk
191
+ try:
192
+ await self.send_start_marker(recipient_id)
193
+ except (WebsocketClosed, ServerError):
194
+ call_state.connection_failed = True # type: ignore[attr-defined]
195
+
176
196
  if cached_audio_bytes:
177
197
  audio_stream = self.chunk_audio(cached_audio_bytes)
178
198
  else:
@@ -189,15 +209,16 @@ class VoiceOutputChannel(OutputChannel):
189
209
  await self.send_audio_bytes(recipient_id, audio_bytes)
190
210
  full_seconds_of_audio = len(collected_audio_bytes) // HERTZ
191
211
  if full_seconds_of_audio > seconds_marker:
192
- await self.send_marker_message(recipient_id)
212
+ await self.send_intermediate_marker(recipient_id)
193
213
  seconds_marker = full_seconds_of_audio
194
214
 
195
215
  except (WebsocketClosed, ServerError):
196
216
  # ignore sending error, and keep collecting and caching audio bytes
197
217
  call_state.connection_failed = True # type: ignore[attr-defined]
198
218
  collected_audio_bytes = RasaAudioBytes(collected_audio_bytes + audio_bytes)
219
+
199
220
  try:
200
- await self.send_marker_message(recipient_id)
221
+ await self.send_end_marker(recipient_id)
201
222
  except (WebsocketClosed, ServerError):
202
223
  # ignore sending error
203
224
  pass
rasa/core/constants.py CHANGED
@@ -110,3 +110,5 @@ UTTER_SOURCE_METADATA_KEY = "utter_source"
110
110
  DOMAIN_GROUND_TRUTH_METADATA_KEY = "domain_ground_truth"
111
111
  ACTIVE_FLOW_METADATA_KEY = "active_flow"
112
112
  STEP_ID_METADATA_KEY = "step_id"
113
+ KEY_IS_CALM_SYSTEM = "is_calm_system"
114
+ KEY_IS_COEXISTENCE_ASSISTANT = "is_coexistence_assistant"
@@ -64,7 +64,7 @@ DEFAULT_LLM_CONFIG = {
64
64
  DEFAULT_RESPONSE_VARIATION_PROMPT_TEMPLATE = """The following is a conversation with
65
65
  an AI assistant. The assistant is helpful, creative, clever, and very friendly.
66
66
  Rephrase the suggested AI response staying close to the original message and retaining
67
- its meaning. Use simple english.
67
+ its meaning. Use simple {{language}}.
68
68
 
69
69
  Context / previous conversation with the user:
70
70
  {{history}}
@@ -164,6 +164,22 @@ class ContextualResponseRephraser(
164
164
  response[PROMPTS] = prompts
165
165
  return response
166
166
 
167
+ @staticmethod
168
+ def get_language_label(tracker: DialogueStateTracker) -> str:
169
+ """Fetches the label of the language to be used for the rephraser.
170
+
171
+ Args:
172
+ tracker: The tracker to get the language from.
173
+
174
+ Returns:
175
+ The label of the current language, or "English" if no language is set.
176
+ """
177
+ return (
178
+ tracker.current_language.label
179
+ if tracker.current_language
180
+ else tracker.default_language.label
181
+ )
182
+
167
183
  def _last_message_if_human(self, tracker: DialogueStateTracker) -> Optional[str]:
168
184
  """Returns the latest message from the tracker.
169
185
 
@@ -281,6 +297,7 @@ class ContextualResponseRephraser(
281
297
  suggested_response=response_text,
282
298
  current_input=current_input,
283
299
  slots=tracker.current_slot_values(),
300
+ language=self.get_language_label(tracker),
284
301
  )
285
302
  log_llm(
286
303
  logger=structlogger,
@@ -1,6 +1,9 @@
1
- import logging
2
1
  from typing import Any, Dict, List, Optional, Text, Union
3
2
 
3
+ import structlog
4
+ from jinja2 import Template
5
+ from pypred import Predicate
6
+
4
7
  import rasa.shared.utils.common
5
8
  import rasa.shared.utils.io
6
9
  from rasa.shared.constants import CHANNEL, RESPONSE_CONDITION
@@ -8,7 +11,7 @@ from rasa.shared.core.domain import Domain
8
11
  from rasa.shared.core.trackers import DialogueStateTracker
9
12
  from rasa.utils.endpoints import EndpointConfig
10
13
 
11
- logger = logging.getLogger(__name__)
14
+ structlogger = structlog.get_logger()
12
15
 
13
16
 
14
17
  class NaturalLanguageGenerator:
@@ -74,7 +77,11 @@ def _create_from_endpoint_config(
74
77
  else:
75
78
  nlg = _load_from_module_name_in_endpoint_config(endpoint_config, domain)
76
79
 
77
- logger.debug(f"Instantiated NLG to '{nlg.__class__.__name__}'.")
80
+ structlogger.debug(
81
+ "rasa.core.nlg.generator.create",
82
+ nlg_class_name=nlg.__class__.__name__,
83
+ event_info=f"Instantiated NLG to '{nlg.__class__.__name__}'.",
84
+ )
78
85
  return nlg
79
86
 
80
87
 
@@ -112,18 +119,15 @@ class ResponseVariationFilter:
112
119
  ) -> bool:
113
120
  """Checks if the conditional response variation matches the filled slots."""
114
121
  constraints = response.get(RESPONSE_CONDITION, [])
115
- for constraint in constraints:
116
- name = constraint["name"]
117
- value = constraint["value"]
118
- filled_slots_value = filled_slots.get(name)
119
- if isinstance(filled_slots_value, str) and isinstance(value, str):
120
- if filled_slots_value.casefold() != value.casefold():
122
+ if isinstance(constraints, str) and not _evaluate_predicate(
123
+ constraints, filled_slots
124
+ ):
125
+ return False
126
+
127
+ elif isinstance(constraints, list):
128
+ for constraint in constraints:
129
+ if not _evaluate_and_deprecate_condition(constraint, filled_slots):
121
130
  return False
122
- # slot values can be of different data types
123
- # such as int, float, bool, etc. hence, this check
124
- # executes when slot values are not strings
125
- elif filled_slots_value != value:
126
- return False
127
131
 
128
132
  return True
129
133
 
@@ -180,7 +184,21 @@ class ResponseVariationFilter:
180
184
  if conditional_no_channel:
181
185
  return conditional_no_channel
182
186
 
183
- return default_no_channel
187
+ if default_no_channel:
188
+ return default_no_channel
189
+
190
+ # if there is no response variation selected,
191
+ # return the internal error response to prevent
192
+ # the bot from staying silent
193
+ structlogger.error(
194
+ "rasa.core.nlg.generator.responses_for_utter_action.no_response",
195
+ utter_action=utter_action,
196
+ event_info=f"No response variation selected for the predicted "
197
+ f"utterance {utter_action}. Please check you have provided "
198
+ f"a default variation and that all the conditions are valid. "
199
+ f"Returning the internal error response.",
200
+ )
201
+ return self.responses.get("utter_internal_error_rasa", [])
184
202
 
185
203
  def get_response_variation_id(
186
204
  self,
@@ -228,3 +246,53 @@ class ResponseVariationFilter:
228
246
  response_ids.add(response_variation_id)
229
247
 
230
248
  return True
249
+
250
+
251
+ def _evaluate_and_deprecate_condition(
252
+ constraint: Dict[Text, Any], filled_slots: Dict[Text, Any]
253
+ ) -> bool:
254
+ """Evaluates the condition of a response variation."""
255
+ rasa.shared.utils.io.raise_deprecation_warning(
256
+ "Using a dictionary as a condition in a response variation is deprecated. "
257
+ "Please use a pypred string predicate instead. "
258
+ "Dictionary conditions will be removed in Rasa Open Source 4.0.0 .",
259
+ warn_until_version="4.0.0",
260
+ )
261
+
262
+ name = constraint["name"]
263
+ value = constraint["value"]
264
+ filled_slots_value = filled_slots.get(name)
265
+ if isinstance(filled_slots_value, str) and isinstance(value, str):
266
+ if filled_slots_value.casefold() != value.casefold():
267
+ return False
268
+ # slot values can be of different data types
269
+ # such as int, float, bool, etc. hence, this check
270
+ # executes when slot values are not strings
271
+ elif filled_slots_value != value:
272
+ return False
273
+
274
+ return True
275
+
276
+
277
+ def _evaluate_predicate(constraint: str, filled_slots: Dict[Text, Any]) -> bool:
278
+ """Evaluates the condition of a response variation."""
279
+ context = {"slots": filled_slots}
280
+ document = context.copy()
281
+ try:
282
+ rendered_template = Template(constraint).render(context)
283
+ predicate = Predicate(rendered_template)
284
+ result = predicate.evaluate(document)
285
+ structlogger.debug(
286
+ "rasa.core.nlg.generator.evaluate_conditional_response_predicate",
287
+ predicate=predicate.description(),
288
+ result=result,
289
+ )
290
+ return result
291
+ except (TypeError, Exception) as e:
292
+ structlogger.error(
293
+ "rasa.core.nlg.generator.evaluate_conditional_response_predicate.error",
294
+ predicate=constraint,
295
+ document=document,
296
+ error=str(e),
297
+ )
298
+ return False
rasa/core/nlg/response.py CHANGED
@@ -49,9 +49,12 @@ class TemplatedNaturalLanguageGenerator(NaturalLanguageGenerator):
49
49
  selected_response = np.random.choice(suitable_responses)
50
50
  condition = selected_response.get(RESPONSE_CONDITION)
51
51
  if condition:
52
- formatted_response_conditions = self._format_response_conditions(
53
- condition
54
- )
52
+ if isinstance(condition, list):
53
+ formatted_response_conditions = (
54
+ self._format_response_conditions(condition)
55
+ )
56
+ else:
57
+ formatted_response_conditions = condition
55
58
  logger.debug(
56
59
  "Selecting response variation with conditions:"
57
60
  f"{formatted_response_conditions}"
@@ -0,0 +1,55 @@
1
+ from typing import Any, Dict, List, Optional, Text
2
+
3
+ from rasa.engine.language import Language
4
+ from rasa.shared.core.flows.constants import KEY_TRANSLATION
5
+
6
+
7
+ def get_translated_text(
8
+ text: Optional[Text],
9
+ translation: Dict[Text, Any],
10
+ language: Optional[Language] = None,
11
+ ) -> Optional[Text]:
12
+ """Get the translated text from the message.
13
+
14
+ Args:
15
+ text: The default text to use if no translation is found.
16
+ translation: The translations for the text.
17
+ language: The language to use for the translation.
18
+
19
+ Returns:
20
+ The translated text if found, otherwise the default text.
21
+ """
22
+ language_code = language.code if language else None
23
+ return translation.get(language_code, text)
24
+
25
+
26
+ def get_translated_buttons(
27
+ buttons: Optional[List[Dict[Text, Any]]], language: Optional[Language] = None
28
+ ) -> Optional[List[Dict[Text, Any]]]:
29
+ """Get the translated buttons from the message.
30
+
31
+ Args:
32
+ buttons: The default buttons to use if no translation is found.
33
+ language: The language to use for the translation.
34
+
35
+ Returns:
36
+ The translated buttons if found; otherwise, the default buttons.
37
+ """
38
+ if buttons is None:
39
+ return None
40
+
41
+ language_code = language.code if language else None
42
+ translated_buttons = []
43
+ for button in buttons:
44
+ translation = button.get(KEY_TRANSLATION, {})
45
+ language_translation = translation.get(language_code, {})
46
+
47
+ # Maintain the original key order to ensure
48
+ # accurate comparisons of BotUtter events.
49
+ translated_button = {
50
+ key: language_translation.get(key, button.get(key))
51
+ for key, value in button.items()
52
+ if key != KEY_TRANSLATION
53
+ }
54
+ translated_buttons.append(translated_button)
55
+ return translated_buttons
@@ -4,7 +4,7 @@ If the answer is not known or cannot be determined from the provided documents o
4
4
  Use the following documents to answer the question:
5
5
  {% for doc in docs %}
6
6
  {{ loop.cycle("*")}}. {{ doc.metadata }}
7
- {{ doc.page_content }}
7
+ {{ doc.text }}
8
8
  {% endfor %}
9
9
 
10
10
  {% if citation_enabled %}
@@ -85,9 +85,8 @@ from rasa.shared.core.flows.steps import (
85
85
  NoOperationFlowStep,
86
86
  SetSlotsFlowStep,
87
87
  )
88
- from rasa.shared.core.flows.steps.collect import SlotRejection
89
88
  from rasa.shared.core.flows.steps.constants import START_STEP
90
- from rasa.shared.core.slots import Slot
89
+ from rasa.shared.core.slots import Slot, SlotRejection
91
90
  from rasa.shared.core.trackers import (
92
91
  DialogueStateTracker,
93
92
  )
@@ -243,7 +242,10 @@ def events_for_collect_step_execution(
243
242
 
244
243
 
245
244
  def trigger_pattern_continue_interrupted(
246
- current_frame: DialogueStackFrame, stack: DialogueStack, flows: FlowsList
245
+ current_frame: DialogueStackFrame,
246
+ stack: DialogueStack,
247
+ flows: FlowsList,
248
+ tracker: DialogueStateTracker,
247
249
  ) -> List[Event]:
248
250
  """Trigger the pattern to continue an interrupted flow if needed."""
249
251
  events: List[Event] = []
@@ -266,7 +268,9 @@ def trigger_pattern_continue_interrupted(
266
268
  ):
267
269
  stack.push(
268
270
  ContinueInterruptedPatternFlowStackFrame(
269
- previous_flow_name=interrupted_user_flow.readable_name(),
271
+ previous_flow_name=interrupted_user_flow.readable_name(
272
+ language=tracker.current_language
273
+ ),
270
274
  )
271
275
  )
272
276
  events.append(
@@ -309,6 +313,9 @@ def trigger_pattern_completed(
309
313
  or isinstance(current_frame, SearchPatternFlowStackFrame)
310
314
  ):
311
315
  completed_flow = current_frame.flow(flows)
316
+ if not completed_flow.run_pattern_completed:
317
+ return
318
+
312
319
  completed_flow_name = completed_flow.readable_name() if completed_flow else None
313
320
  stack.push(
314
321
  CompletedPatternFlowStackFrame(
@@ -670,7 +677,7 @@ def _run_end_step(
670
677
  trigger_pattern_clarification(current_frame, stack, flows)
671
678
  else:
672
679
  resumed_events = trigger_pattern_continue_interrupted(
673
- current_frame, stack, flows
680
+ current_frame, stack, flows, tracker
674
681
  )
675
682
  reset_events: List[Event] = reset_scoped_slots(current_frame, flow, tracker)
676
683
  return ContinueFlowWithNextStep(
rasa/core/processor.py CHANGED
@@ -25,6 +25,7 @@ from rasa.core.channels.channel import (
25
25
  OutputChannel,
26
26
  UserMessage,
27
27
  )
28
+ from rasa.core.constants import KEY_IS_CALM_SYSTEM, KEY_IS_COEXISTENCE_ASSISTANT
28
29
  from rasa.core.http_interpreter import RasaNLUHttpInterpreter
29
30
  from rasa.core.lock_store import LockStore
30
31
  from rasa.core.nlg import NaturalLanguageGenerator
@@ -35,6 +36,12 @@ from rasa.dialogue_understanding.commands import (
35
36
  NoopCommand,
36
37
  SetSlotCommand,
37
38
  )
39
+ from rasa.dialogue_understanding.commands.utils import (
40
+ create_validate_frames_from_slot_set_events,
41
+ )
42
+ from rasa.dialogue_understanding.patterns.validate_slot import (
43
+ ValidateSlotPatternFlowStackFrame,
44
+ )
38
45
  from rasa.dialogue_understanding.utils import add_commands_to_message_parse_data
39
46
  from rasa.engine import loader
40
47
  from rasa.engine.constants import (
@@ -201,10 +208,7 @@ class MessageProcessor:
201
208
  )
202
209
  return None
203
210
 
204
- if not self.message_contains_commands(tracker.latest_message):
205
- tracker = await self.run_action_extract_slots(
206
- message.output_channel, tracker
207
- )
211
+ tracker = await self.run_action_extract_slots(message.output_channel, tracker)
208
212
 
209
213
  await self._run_prediction_loop(message.output_channel, tracker)
210
214
 
@@ -218,7 +222,9 @@ class MessageProcessor:
218
222
  return None
219
223
 
220
224
  async def run_action_extract_slots(
221
- self, output_channel: OutputChannel, tracker: DialogueStateTracker
225
+ self,
226
+ output_channel: OutputChannel,
227
+ tracker: DialogueStateTracker,
222
228
  ) -> DialogueStateTracker:
223
229
  """Run action to extract slots and update the tracker accordingly.
224
230
 
@@ -233,6 +239,10 @@ class MessageProcessor:
233
239
  ACTION_EXTRACT_SLOTS, self.domain, self.action_endpoint
234
240
  )
235
241
  metadata = await self._add_flows_to_metadata()
242
+ metadata[KEY_IS_CALM_SYSTEM] = self.message_contains_commands(
243
+ tracker.latest_message
244
+ )
245
+ metadata[KEY_IS_COEXISTENCE_ASSISTANT] = self._is_coexistence_assistant(tracker)
236
246
 
237
247
  extraction_events = await action_extract_slots.run(
238
248
  output_channel, self.nlg, tracker, self.domain, metadata
@@ -1251,6 +1261,12 @@ class MessageProcessor:
1251
1261
  # events and return values are used to update
1252
1262
  # the tracker state after an action has been taken
1253
1263
  try:
1264
+ validate_frames: List[ValidateSlotPatternFlowStackFrame] = []
1265
+ # check if the last action was a correction action
1266
+ # before validating the corrected slots
1267
+ if tracker.latest_action_name == ACTION_CORRECT_FLOW_SLOT:
1268
+ tracker, validate_frames = self.validate_corrected_slots(tracker)
1269
+
1254
1270
  # Use temporary tracker as we might need to discard the policy events in
1255
1271
  # case of a rejection.
1256
1272
  temporary_tracker = tracker.copy()
@@ -1262,6 +1278,12 @@ class MessageProcessor:
1262
1278
 
1263
1279
  if isinstance(action, FormAction):
1264
1280
  flows_metadata = await self._add_flows_to_metadata()
1281
+ flows_metadata[KEY_IS_CALM_SYSTEM] = self.message_contains_commands(
1282
+ temporary_tracker.latest_message
1283
+ )
1284
+ flows_metadata[KEY_IS_COEXISTENCE_ASSISTANT] = (
1285
+ self._is_coexistence_assistant(temporary_tracker)
1286
+ )
1265
1287
  metadata = prediction.action_metadata or {}
1266
1288
  metadata.update(flows_metadata)
1267
1289
 
@@ -1276,6 +1298,14 @@ class MessageProcessor:
1276
1298
  events = await action.run(
1277
1299
  output_channel, nlg, temporary_tracker, self.domain
1278
1300
  )
1301
+
1302
+ if validate_frames:
1303
+ stack = tracker.stack
1304
+ for frame in validate_frames:
1305
+ stack.push(frame)
1306
+ new_events = tracker.create_stack_updated_events(stack)
1307
+ tracker.update_with_events(new_events)
1308
+
1279
1309
  self._log_action_and_events_on_tracker(tracker, action, events, prediction)
1280
1310
  except ActionExecutionRejection:
1281
1311
  events = [
@@ -1479,18 +1509,51 @@ class MessageProcessor:
1479
1509
  Returns:
1480
1510
  bool: True if any node in the graph schema uses `FlowPolicy`.
1481
1511
  """
1512
+ flow_policy_class_path = "rasa.core.policies.flow_policy.FlowPolicy"
1513
+ return self._is_component_present_in_graph_nodes(flow_policy_class_path)
1514
+
1515
+ @staticmethod
1516
+ def _is_coexistence_assistant(tracker: DialogueStateTracker) -> bool:
1517
+ """Inspect the tracker to decide if we are in coexistence.
1518
+
1519
+ Returns:
1520
+ bool: True if the tracker contains the routine slot.
1521
+ """
1522
+ return tracker.slots.get(ROUTE_TO_CALM_SLOT) is not None
1523
+
1524
+ def _is_component_present_in_graph_nodes(self, component_path: Text) -> bool:
1525
+ """Check if a component is present in the graph nodes.
1526
+
1527
+ Args:
1528
+ component_path: The path of the component to check for.
1529
+
1530
+ Returns:
1531
+ `True` if the component is present in the graph nodes, `False` otherwise.
1532
+ """
1482
1533
  # Get the graph schema's nodes from the graph runner.
1483
1534
  nodes: dict[str, Any] = self.graph_runner._graph_schema.nodes # type: ignore[attr-defined]
1484
1535
 
1485
- flow_policy_class_path = "rasa.core.policies.flow_policy.FlowPolicy"
1486
- # Iterate over the nodes and check if any node uses `FlowPolicy`.
1487
1536
  for node_name, schema_node in nodes.items():
1488
1537
  if (
1489
1538
  schema_node.uses is not None
1490
1539
  and f"{schema_node.uses.__module__}.{schema_node.uses.__name__}"
1491
- == flow_policy_class_path
1540
+ == component_path
1492
1541
  ):
1493
1542
  return True
1494
1543
 
1495
- # Return False if no node is found using `FlowPolicy`.
1496
1544
  return False
1545
+
1546
+ def validate_corrected_slots(
1547
+ self,
1548
+ tracker: DialogueStateTracker,
1549
+ ) -> Tuple[DialogueStateTracker, List[ValidateSlotPatternFlowStackFrame]]:
1550
+ """Validate the slots that were corrected in the tracker."""
1551
+ prior_tracker_events = list(reversed(tracker.events))
1552
+ tracker, validate_frames = create_validate_frames_from_slot_set_events(
1553
+ tracker,
1554
+ prior_tracker_events,
1555
+ should_break=True,
1556
+ update_corrected_slots=True,
1557
+ )
1558
+
1559
+ return tracker, validate_frames
@@ -5,6 +5,10 @@ from dataclasses import dataclass
5
5
  from typing import Any, Dict, List, Optional, Text
6
6
 
7
7
  from rasa.dialogue_understanding.commands.command import Command
8
+ from rasa.dialogue_understanding.commands.command_syntax_manager import (
9
+ CommandSyntaxManager,
10
+ CommandSyntaxVersion,
11
+ )
8
12
  from rasa.dialogue_understanding.patterns.cannot_handle import (
9
13
  CannotHandlePatternFlowStackFrame,
10
14
  )
@@ -74,7 +78,14 @@ class CannotHandleCommand(Command):
74
78
 
75
79
  def to_dsl(self) -> str:
76
80
  """Converts the command to a DSL string."""
77
- return "CannotHandle()"
81
+ mapper = {
82
+ CommandSyntaxVersion.v1: "CannotHandle()",
83
+ CommandSyntaxVersion.v2: "cannot handle",
84
+ }
85
+ return mapper.get(
86
+ CommandSyntaxManager.get_syntax_version(),
87
+ mapper[CommandSyntaxManager.get_default_syntax_version()],
88
+ )
78
89
 
79
90
  @classmethod
80
91
  def from_dsl(cls, match: re.Match, **kwargs: Any) -> CannotHandleCommand:
@@ -86,4 +97,11 @@ class CannotHandleCommand(Command):
86
97
 
87
98
  @staticmethod
88
99
  def regex_pattern() -> str:
89
- return r"CannotHandle\(\)"
100
+ mapper = {
101
+ CommandSyntaxVersion.v1: r"CannotHandle\(\)",
102
+ CommandSyntaxVersion.v2: r"^[^\w]*cannot handle$",
103
+ }
104
+ return mapper.get(
105
+ CommandSyntaxManager.get_syntax_version(),
106
+ mapper[CommandSyntaxManager.get_default_syntax_version()],
107
+ )
@@ -8,12 +8,14 @@ from typing import Any, Dict, List
8
8
  import structlog
9
9
 
10
10
  from rasa.dialogue_understanding.commands.command import Command
11
+ from rasa.dialogue_understanding.commands.command_syntax_manager import (
12
+ CommandSyntaxManager,
13
+ CommandSyntaxVersion,
14
+ )
11
15
  from rasa.dialogue_understanding.patterns.cancel import CancelPatternFlowStackFrame
12
16
  from rasa.dialogue_understanding.patterns.clarify import ClarifyPatternFlowStackFrame
13
17
  from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack
14
- from rasa.dialogue_understanding.stack.frames import (
15
- UserFlowStackFrame,
16
- )
18
+ from rasa.dialogue_understanding.stack.frames import UserFlowStackFrame
17
19
  from rasa.dialogue_understanding.stack.frames.flow_stack_frame import FlowStackFrameType
18
20
  from rasa.dialogue_understanding.stack.utils import top_user_flow_frame
19
21
  from rasa.shared.core.events import Event, FlowCancelled
@@ -111,7 +113,9 @@ class CancelFlowCommand(Command):
111
113
 
112
114
  stack.push(
113
115
  CancelPatternFlowStackFrame(
114
- canceled_name=current_flow.readable_name(),
116
+ canceled_name=current_flow.readable_name(
117
+ language=tracker.current_language
118
+ ),
115
119
  canceled_frames=canceled_frames,
116
120
  )
117
121
  )
@@ -144,7 +148,14 @@ class CancelFlowCommand(Command):
144
148
 
145
149
  def to_dsl(self) -> str:
146
150
  """Converts the command to a DSL string."""
147
- return "CancelFlow()"
151
+ mapper = {
152
+ CommandSyntaxVersion.v1: "CancelFlow()",
153
+ CommandSyntaxVersion.v2: "cancel flow",
154
+ }
155
+ return mapper.get(
156
+ CommandSyntaxManager.get_syntax_version(),
157
+ mapper[CommandSyntaxManager.get_default_syntax_version()],
158
+ )
148
159
 
149
160
  @classmethod
150
161
  def from_dsl(cls, match: re.Match, **kwargs: Any) -> CancelFlowCommand:
@@ -153,7 +164,14 @@ class CancelFlowCommand(Command):
153
164
 
154
165
  @staticmethod
155
166
  def regex_pattern() -> str:
156
- return r"CancelFlow\(\)"
167
+ mapper = {
168
+ CommandSyntaxVersion.v1: r"CancelFlow\(\)",
169
+ CommandSyntaxVersion.v2: r"^[^\w]*cancel flow$",
170
+ }
171
+ return mapper.get(
172
+ CommandSyntaxManager.get_syntax_version(),
173
+ mapper[CommandSyntaxManager.get_default_syntax_version()],
174
+ )
157
175
 
158
176
 
159
177
  def cancel_all_pending_clarification_options(