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
@@ -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(
@@ -283,8 +287,13 @@ def trigger_pattern_clarification(
283
287
  if not isinstance(current_frame, UserFlowStackFrame):
284
288
  return None
285
289
 
286
- if current_frame.frame_type == FlowStackFrameType.CALL:
287
- # we want to return to the flow that called the current flow
290
+ if current_frame.frame_type in [
291
+ FlowStackFrameType.CALL,
292
+ FlowStackFrameType.INTERRUPT,
293
+ ]:
294
+ # we want to return to the flow that called
295
+ # the current flow or the flow that was interrupted
296
+ # by the current flow
288
297
  return None
289
298
 
290
299
  pending_flows = [
@@ -309,6 +318,9 @@ def trigger_pattern_completed(
309
318
  or isinstance(current_frame, SearchPatternFlowStackFrame)
310
319
  ):
311
320
  completed_flow = current_frame.flow(flows)
321
+ if not completed_flow.run_pattern_completed:
322
+ return
323
+
312
324
  completed_flow_name = completed_flow.readable_name() if completed_flow else None
313
325
  stack.push(
314
326
  CompletedPatternFlowStackFrame(
@@ -670,7 +682,7 @@ def _run_end_step(
670
682
  trigger_pattern_clarification(current_frame, stack, flows)
671
683
  else:
672
684
  resumed_events = trigger_pattern_continue_interrupted(
673
- current_frame, stack, flows
685
+ current_frame, stack, flows, tracker
674
686
  )
675
687
  reset_events: List[Event] = reset_scoped_slots(current_frame, flow, tracker)
676
688
  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,50 @@ 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
+ )
1557
+
1558
+ 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"""^[\s\W\d]*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"""^[\s\W\d]*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(
@@ -5,6 +5,10 @@ from dataclasses import dataclass
5
5
  from typing import Any, Dict, List
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.shared.core.events import Event
9
13
  from rasa.shared.core.flows import FlowsList
10
14
  from rasa.shared.core.trackers import DialogueStateTracker
@@ -48,7 +52,14 @@ class ChangeFlowCommand(Command):
48
52
 
49
53
  def to_dsl(self) -> str:
50
54
  """Converts the command to a DSL string."""
51
- return "ChangeFlow()"
55
+ mapper = {
56
+ CommandSyntaxVersion.v1: "ChangeFlow()",
57
+ CommandSyntaxVersion.v2: "change",
58
+ }
59
+ return mapper.get(
60
+ CommandSyntaxManager.get_syntax_version(),
61
+ mapper[CommandSyntaxManager.get_default_syntax_version()],
62
+ )
52
63
 
53
64
  @staticmethod
54
65
  def from_dsl(match: re.Match, **kwargs: Any) -> ChangeFlowCommand:
@@ -57,4 +68,11 @@ class ChangeFlowCommand(Command):
57
68
 
58
69
  @staticmethod
59
70
  def regex_pattern() -> str:
60
- return r"ChangeFlow\(\)"
71
+ mapper = {
72
+ CommandSyntaxVersion.v1: r"ChangeFlow\(\)",
73
+ CommandSyntaxVersion.v2: r"""^[\s\W\d]*change['"`]*$""",
74
+ }
75
+ return mapper.get(
76
+ CommandSyntaxManager.get_syntax_version(),
77
+ mapper[CommandSyntaxManager.get_default_syntax_version()],
78
+ )
@@ -4,6 +4,10 @@ import re
4
4
  from dataclasses import dataclass
5
5
  from typing import Any, Dict, List
6
6
 
7
+ from rasa.dialogue_understanding.commands.command_syntax_manager import (
8
+ CommandSyntaxManager,
9
+ CommandSyntaxVersion,
10
+ )
7
11
  from rasa.dialogue_understanding.commands.free_form_answer_command import (
8
12
  FreeFormAnswerCommand,
9
13
  )
@@ -59,7 +63,14 @@ class ChitChatAnswerCommand(FreeFormAnswerCommand):
59
63
 
60
64
  def to_dsl(self) -> str:
61
65
  """Converts the command to a DSL string."""
62
- return "ChitChat()"
66
+ mapper = {
67
+ CommandSyntaxVersion.v1: "ChitChat()",
68
+ CommandSyntaxVersion.v2: "offtopic reply",
69
+ }
70
+ return mapper.get(
71
+ CommandSyntaxManager.get_syntax_version(),
72
+ mapper[CommandSyntaxManager.get_default_syntax_version()],
73
+ )
63
74
 
64
75
  @classmethod
65
76
  def from_dsl(cls, match: re.Match, **kwargs: Any) -> ChitChatAnswerCommand:
@@ -68,4 +79,11 @@ class ChitChatAnswerCommand(FreeFormAnswerCommand):
68
79
 
69
80
  @staticmethod
70
81
  def regex_pattern() -> str:
71
- return r"ChitChat\(\)"
82
+ mapper = {
83
+ CommandSyntaxVersion.v1: r"ChitChat\(\)",
84
+ CommandSyntaxVersion.v2: r"""^[\s\W\d]*offtopic reply['"`]*$""",
85
+ }
86
+ return mapper.get(
87
+ CommandSyntaxManager.get_syntax_version(),
88
+ mapper[CommandSyntaxManager.get_default_syntax_version()],
89
+ )
@@ -7,6 +7,10 @@ from typing import Any, Dict, List, Optional
7
7
  import structlog
8
8
 
9
9
  from rasa.dialogue_understanding.commands.command import Command
10
+ from rasa.dialogue_understanding.commands.command_syntax_manager import (
11
+ CommandSyntaxManager,
12
+ CommandSyntaxVersion,
13
+ )
10
14
  from rasa.dialogue_understanding.commands.utils import extract_cleaned_options
11
15
  from rasa.dialogue_understanding.patterns.clarify import ClarifyPatternFlowStackFrame
12
16
  from rasa.shared.core.events import Event
@@ -74,7 +78,13 @@ class ClarifyCommand(Command):
74
78
 
75
79
  stack = tracker.stack
76
80
  relevant_flows = [all_flows.flow_by_id(opt) for opt in clean_options]
77
- names = [flow.readable_name() for flow in relevant_flows if flow is not None]
81
+
82
+ names = [
83
+ flow.readable_name(language=tracker.current_language)
84
+ for flow in relevant_flows
85
+ if flow is not None
86
+ ]
87
+
78
88
  stack.push(ClarifyPatternFlowStackFrame(names=names))
79
89
  return tracker.create_stack_updated_events(stack)
80
90
 
@@ -89,7 +99,14 @@ class ClarifyCommand(Command):
89
99
 
90
100
  def to_dsl(self) -> str:
91
101
  """Converts the command to a DSL string."""
92
- return f"Clarify({', '.join(self.options)})"
102
+ mapper = {
103
+ CommandSyntaxVersion.v1: f"Clarify({', '.join(self.options)})",
104
+ CommandSyntaxVersion.v2: f"disambiguate flows {' '.join(self.options)}",
105
+ }
106
+ return mapper.get(
107
+ CommandSyntaxManager.get_syntax_version(),
108
+ mapper[CommandSyntaxManager.get_default_syntax_version()],
109
+ )
93
110
 
94
111
  @classmethod
95
112
  def from_dsl(cls, match: re.Match, **kwargs: Any) -> Optional[ClarifyCommand]:
@@ -99,4 +116,13 @@ class ClarifyCommand(Command):
99
116
 
100
117
  @staticmethod
101
118
  def regex_pattern() -> str:
102
- return r"Clarify\(([\"\'a-zA-Z0-9_, ]*)\)"
119
+ mapper = {
120
+ CommandSyntaxVersion.v1: r"Clarify\(([\"\'a-zA-Z0-9_, ]*)\)",
121
+ CommandSyntaxVersion.v2: (
122
+ r"""^[\s\W\d]*disambiguate flows (["'a-zA-Z0-9_, ]*)['"`]*$"""
123
+ ),
124
+ }
125
+ return mapper.get(
126
+ CommandSyntaxManager.get_syntax_version(),
127
+ mapper[CommandSyntaxManager.get_default_syntax_version()],
128
+ )
@@ -1,9 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import dataclasses
4
- import re
5
4
  from dataclasses import dataclass
6
- from typing import Any, Dict, List, Optional
5
+ from typing import Any, Dict, List
7
6
 
8
7
  import rasa.shared.utils.common
9
8
  from rasa.shared.core.events import Event
@@ -85,17 +84,3 @@ class Command:
85
84
  The events to apply to the tracker.
86
85
  """
87
86
  raise NotImplementedError()
88
-
89
- def to_dsl(self) -> str:
90
- """Converts the command to a DSL string."""
91
- raise NotImplementedError()
92
-
93
- @classmethod
94
- def from_dsl(cls, match: re.Match, **kwargs: Any) -> Optional[Command]:
95
- """Converts the DSL string to a command."""
96
- raise NotImplementedError()
97
-
98
- @staticmethod
99
- def regex_pattern() -> str:
100
- """Returns the regex pattern for the command."""
101
- raise NotImplementedError()
@@ -0,0 +1,55 @@
1
+ from enum import Enum
2
+ from typing import Optional
3
+
4
+ import structlog
5
+
6
+
7
+ class CommandSyntaxVersion(Enum):
8
+ """Defines different syntax versions for commands."""
9
+
10
+ v1 = "v1"
11
+ v2 = "v2"
12
+
13
+
14
+ structlogger = structlog.get_logger()
15
+
16
+
17
+ class CommandSyntaxManager:
18
+ """A class to manage the command syntax version. It is used to set and get the
19
+ command syntax version. This class provides a way to introduce new syntax versions
20
+ for commands in the future. Hence, it is for internal use only.
21
+ """
22
+
23
+ _version = None # Directly store the version as a class attribute
24
+
25
+ @classmethod
26
+ def set_syntax_version(cls, version: CommandSyntaxVersion) -> None:
27
+ """Sets the command syntax version on the class itself.
28
+ This method is called only once at the time of LLMCommandGenerator
29
+ initialization to set the command syntax version, which ensures that the command
30
+ syntax version remains consistent throughout the lifetime of the generator.
31
+ """
32
+ if cls._version:
33
+ structlogger.debug(
34
+ "command_syntax_manager.syntax_version_already_set",
35
+ event_info=(
36
+ f"The command syntax version has already been set. Overwriting "
37
+ f"the existing version with the new version - {version}."
38
+ ),
39
+ )
40
+ cls._version = version
41
+
42
+ @classmethod
43
+ def get_syntax_version(cls) -> Optional[CommandSyntaxVersion]:
44
+ """Fetches the stored command syntax version."""
45
+ return cls._version
46
+
47
+ @staticmethod
48
+ def get_default_syntax_version() -> CommandSyntaxVersion:
49
+ """Returns the default command syntax version."""
50
+ return CommandSyntaxVersion.v1
51
+
52
+ @classmethod
53
+ def reset_syntax_version(cls) -> None:
54
+ """Resets the command syntax version. Implemented for use in testing."""
55
+ cls._version = None
@@ -14,7 +14,6 @@ from rasa.dialogue_understanding.patterns.handle_digressions import (
14
14
  )
15
15
  from rasa.dialogue_understanding.stack.utils import (
16
16
  top_flow_frame,
17
- user_flows_on_the_stack,
18
17
  )
19
18
  from rasa.shared.core.events import Event
20
19
  from rasa.shared.core.flows import FlowsList
@@ -71,12 +70,7 @@ class HandleDigressionsCommand(Command):
71
70
  stack = tracker.stack
72
71
  original_stack = original_tracker.stack
73
72
 
74
- if self.flow in user_flows_on_the_stack(stack):
75
- structlogger.debug(
76
- "command_executor.skip_command.already_started_flow", command=self
77
- )
78
- return []
79
- elif self.flow not in all_flows.flow_ids:
73
+ if self.flow not in all_flows.flow_ids:
80
74
  structlogger.debug(
81
75
  "command_executor.push_cannot_handle.start_invalid_flow_id",
82
76
  command=self,
@@ -7,6 +7,10 @@ from typing import Any, Dict, List
7
7
  import structlog
8
8
 
9
9
  from rasa.dialogue_understanding.commands.command import Command
10
+ from rasa.dialogue_understanding.commands.command_syntax_manager import (
11
+ CommandSyntaxManager,
12
+ CommandSyntaxVersion,
13
+ )
10
14
  from rasa.dialogue_understanding.patterns.human_handoff import (
11
15
  HumanHandoffPatternFlowStackFrame,
12
16
  )
@@ -66,7 +70,14 @@ class HumanHandoffCommand(Command):
66
70
 
67
71
  def to_dsl(self) -> str:
68
72
  """Converts the command to a DSL string."""
69
- return "HumanHandoff()"
73
+ mapper = {
74
+ CommandSyntaxVersion.v1: "HumanHandoff()",
75
+ CommandSyntaxVersion.v2: "hand over",
76
+ }
77
+ return mapper.get(
78
+ CommandSyntaxManager.get_syntax_version(),
79
+ mapper[CommandSyntaxManager.get_default_syntax_version()],
80
+ )
70
81
 
71
82
  @classmethod
72
83
  def from_dsl(cls, match: re.Match, **kwargs: Any) -> HumanHandoffCommand:
@@ -75,4 +86,11 @@ class HumanHandoffCommand(Command):
75
86
 
76
87
  @staticmethod
77
88
  def regex_pattern() -> str:
78
- return r"HumanHandoff\(\)"
89
+ mapper = {
90
+ CommandSyntaxVersion.v1: r"HumanHandoff\(\)",
91
+ CommandSyntaxVersion.v2: r"""^[\s\W\d]*hand over['"`]*$""",
92
+ }
93
+ return mapper.get(
94
+ CommandSyntaxManager.get_syntax_version(),
95
+ mapper[CommandSyntaxManager.get_default_syntax_version()],
96
+ )