rasa-pro 3.12.0.dev12__py3-none-any.whl → 3.12.0.dev13__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 (71) hide show
  1. rasa/cli/inspect.py +20 -1
  2. rasa/cli/shell.py +3 -3
  3. rasa/core/actions/action.py +20 -7
  4. rasa/core/actions/action_handle_digressions.py +142 -0
  5. rasa/core/actions/forms.py +10 -5
  6. rasa/core/channels/__init__.py +2 -0
  7. rasa/core/channels/voice_ready/audiocodes.py +42 -23
  8. rasa/core/channels/voice_stream/browser_audio.py +1 -0
  9. rasa/core/channels/voice_stream/call_state.py +7 -1
  10. rasa/core/channels/voice_stream/genesys.py +331 -0
  11. rasa/core/channels/voice_stream/tts/azure.py +2 -1
  12. rasa/core/channels/voice_stream/tts/cartesia.py +16 -3
  13. rasa/core/channels/voice_stream/twilio_media_streams.py +2 -1
  14. rasa/core/channels/voice_stream/voice_channel.py +2 -1
  15. rasa/core/migrate.py +2 -2
  16. rasa/core/policies/flows/flow_executor.py +36 -42
  17. rasa/core/run.py +4 -3
  18. rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -2
  19. rasa/dialogue_understanding/commands/cancel_flow_command.py +62 -4
  20. rasa/dialogue_understanding/commands/change_flow_command.py +2 -2
  21. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -2
  22. rasa/dialogue_understanding/commands/clarify_command.py +2 -2
  23. rasa/dialogue_understanding/commands/correct_slots_command.py +11 -2
  24. rasa/dialogue_understanding/commands/handle_digressions_command.py +150 -0
  25. rasa/dialogue_understanding/commands/human_handoff_command.py +2 -2
  26. rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -2
  27. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -2
  28. rasa/dialogue_understanding/commands/set_slot_command.py +7 -15
  29. rasa/dialogue_understanding/commands/skip_question_command.py +2 -2
  30. rasa/dialogue_understanding/commands/start_flow_command.py +43 -2
  31. rasa/dialogue_understanding/commands/utils.py +1 -1
  32. rasa/dialogue_understanding/constants.py +1 -0
  33. rasa/dialogue_understanding/generator/command_generator.py +110 -73
  34. rasa/dialogue_understanding/generator/command_parser.py +1 -1
  35. rasa/dialogue_understanding/generator/llm_based_command_generator.py +161 -3
  36. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +10 -2
  37. rasa/dialogue_understanding/generator/nlu_command_adapter.py +44 -3
  38. rasa/dialogue_understanding/generator/single_step/command_prompt_template.jinja2 +53 -79
  39. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +11 -19
  40. rasa/dialogue_understanding/generator/utils.py +32 -1
  41. rasa/dialogue_understanding/patterns/correction.py +13 -1
  42. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +62 -2
  43. rasa/dialogue_understanding/patterns/handle_digressions.py +81 -0
  44. rasa/dialogue_understanding/processor/command_processor.py +115 -28
  45. rasa/dialogue_understanding/utils.py +31 -0
  46. rasa/dialogue_understanding_test/README.md +50 -0
  47. rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +3 -3
  48. rasa/model_manager/warm_rasa_process.py +0 -1
  49. rasa/model_training.py +24 -27
  50. rasa/shared/core/constants.py +28 -3
  51. rasa/shared/core/domain.py +13 -20
  52. rasa/shared/core/events.py +13 -2
  53. rasa/shared/core/flows/flow.py +17 -0
  54. rasa/shared/core/flows/flows_yaml_schema.json +38 -0
  55. rasa/shared/core/flows/steps/collect.py +18 -1
  56. rasa/shared/core/flows/utils.py +16 -1
  57. rasa/shared/core/slot_mappings.py +144 -108
  58. rasa/shared/core/slots.py +23 -2
  59. rasa/shared/core/trackers.py +3 -1
  60. rasa/shared/nlu/constants.py +1 -0
  61. rasa/shared/utils/llm.py +1 -1
  62. rasa/shared/utils/schemas/domain.yml +0 -1
  63. rasa/telemetry.py +43 -13
  64. rasa/utils/common.py +0 -1
  65. rasa/validator.py +189 -82
  66. rasa/version.py +1 -1
  67. {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0.dev13.dist-info}/METADATA +1 -1
  68. {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0.dev13.dist-info}/RECORD +71 -67
  69. {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0.dev13.dist-info}/NOTICE +0 -0
  70. {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0.dev13.dist-info}/WHEEL +0 -0
  71. {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0.dev13.dist-info}/entry_points.txt +0 -0
@@ -46,10 +46,8 @@ from rasa.shared.constants import (
46
46
  )
47
47
  from rasa.shared.core.constants import (
48
48
  ACTION_SHOULD_SEND_DOMAIN,
49
- ACTIVE_LOOP,
49
+ KEY_MAPPING_TYPE,
50
50
  KNOWLEDGE_BASE_SLOT_NAMES,
51
- MAPPING_CONDITIONS,
52
- MAPPING_TYPE,
53
51
  SLOT_MAPPINGS,
54
52
  SlotMappingType,
55
53
  )
@@ -290,8 +288,6 @@ class Domain:
290
288
  responses = data.get(KEY_RESPONSES, {})
291
289
 
292
290
  domain_slots = data.get(KEY_SLOTS, {})
293
- if domain_slots:
294
- rasa.shared.core.slot_mappings.validate_slot_mappings(domain_slots)
295
291
  slots = cls.collect_slots(domain_slots)
296
292
  domain_actions = data.get(KEY_ACTIONS, [])
297
293
  actions = cls._collect_action_names(domain_actions)
@@ -596,7 +592,7 @@ class Domain:
596
592
  ),
597
593
  )
598
594
  slot_dict[slot_name][SLOT_MAPPINGS] = [
599
- {MAPPING_TYPE: SlotMappingType.FROM_LLM.value}
595
+ {KEY_MAPPING_TYPE: SlotMappingType.FROM_LLM.value}
600
596
  ]
601
597
 
602
598
  slot = slot_class(slot_name, **slot_dict[slot_name])
@@ -1569,21 +1565,18 @@ class Domain:
1569
1565
  matching_entities = []
1570
1566
 
1571
1567
  for mapping in slot.mappings:
1572
- mapping_conditions = mapping.get(MAPPING_CONDITIONS)
1573
- if mapping[MAPPING_TYPE] != str(SlotMappingType.FROM_ENTITY) or (
1568
+ mapping_conditions = mapping.conditions
1569
+ if mapping.type != SlotMappingType.FROM_ENTITY or (
1574
1570
  mapping_conditions
1575
- and mapping_conditions[0].get(ACTIVE_LOOP) is not None
1571
+ and mapping_conditions[0].active_loop is not None
1576
1572
  ):
1577
1573
  continue
1578
1574
 
1579
1575
  for entity in entities:
1580
1576
  if (
1581
- entity.get(ENTITY_ATTRIBUTE_TYPE)
1582
- == mapping.get(ENTITY_ATTRIBUTE_TYPE)
1583
- and entity.get(ENTITY_ATTRIBUTE_ROLE)
1584
- == mapping.get(ENTITY_ATTRIBUTE_ROLE)
1585
- and entity.get(ENTITY_ATTRIBUTE_GROUP)
1586
- == mapping.get(ENTITY_ATTRIBUTE_GROUP)
1577
+ entity.get(ENTITY_ATTRIBUTE_TYPE) == mapping.entity
1578
+ and entity.get(ENTITY_ATTRIBUTE_ROLE) == mapping.role
1579
+ and entity.get(ENTITY_ATTRIBUTE_GROUP) == mapping.group
1587
1580
  ):
1588
1581
  matching_entities.append(entity.get("value"))
1589
1582
 
@@ -2015,19 +2008,19 @@ class Domain:
2015
2008
  is the total number of mappings which have conditions attached.
2016
2009
  """
2017
2010
  total_mappings = 0
2018
- custom_mappings = 0
2011
+ controlled_mappings = 0
2019
2012
  conditional_mappings = 0
2020
2013
 
2021
2014
  for slot in self.slots:
2022
2015
  total_mappings += len(slot.mappings)
2023
2016
  for mapping in slot.mappings:
2024
- if mapping[MAPPING_TYPE] == str(SlotMappingType.CUSTOM):
2025
- custom_mappings += 1
2017
+ if mapping.type == SlotMappingType.CONTROLLED:
2018
+ controlled_mappings += 1
2026
2019
 
2027
- if MAPPING_CONDITIONS in mapping:
2020
+ if mapping.conditions:
2028
2021
  conditional_mappings += 1
2029
2022
 
2030
- return (total_mappings, custom_mappings, conditional_mappings)
2023
+ return total_mappings, controlled_mappings, conditional_mappings
2031
2024
 
2032
2025
  def does_custom_action_explicitly_need_domain(self, action_name: Text) -> bool:
2033
2026
  """Assert if action has explicitly stated that it needs domain.
@@ -1032,6 +1032,7 @@ class SlotSet(Event):
1032
1032
  value: Optional[Any] = None,
1033
1033
  timestamp: Optional[float] = None,
1034
1034
  metadata: Optional[Dict[Text, Any]] = None,
1035
+ filled_by: Optional[str] = None,
1035
1036
  ) -> None:
1036
1037
  """Creates event to set slot.
1037
1038
 
@@ -1043,6 +1044,7 @@ class SlotSet(Event):
1043
1044
  """
1044
1045
  self.key = key
1045
1046
  self.value = value
1047
+ self._filled_by = filled_by
1046
1048
  super().__init__(timestamp, metadata)
1047
1049
 
1048
1050
  def __repr__(self) -> Text:
@@ -1060,6 +1062,14 @@ class SlotSet(Event):
1060
1062
 
1061
1063
  return (self.key, self.value) == (other.key, other.value)
1062
1064
 
1065
+ @property
1066
+ def filled_by(self) -> Optional[str]:
1067
+ return self._filled_by
1068
+
1069
+ @filled_by.setter
1070
+ def filled_by(self, value: str) -> None:
1071
+ self._filled_by = value
1072
+
1063
1073
  def as_story_string(self) -> Text:
1064
1074
  """Returns text representation of event."""
1065
1075
  props = json.dumps({self.key: self.value}, ensure_ascii=False)
@@ -1081,7 +1091,7 @@ class SlotSet(Event):
1081
1091
  def as_dict(self) -> Dict[Text, Any]:
1082
1092
  """Returns serialized event."""
1083
1093
  d = super().as_dict()
1084
- d.update({"name": self.key, "value": self.value})
1094
+ d.update({"name": self.key, "value": self.value, "filled_by": self.filled_by})
1085
1095
  return d
1086
1096
 
1087
1097
  @classmethod
@@ -1092,13 +1102,14 @@ class SlotSet(Event):
1092
1102
  parameters.get("value"),
1093
1103
  parameters.get("timestamp"),
1094
1104
  parameters.get("metadata"),
1105
+ filled_by=parameters.get("filled_by"),
1095
1106
  )
1096
1107
  except KeyError as e:
1097
1108
  raise ValueError(f"Failed to parse set slot event. {e}")
1098
1109
 
1099
1110
  def apply_to(self, tracker: "DialogueStateTracker") -> None:
1100
1111
  """Applies event to current conversation state."""
1101
- tracker._set_slot(self.key, self.value)
1112
+ tracker._set_slot(self.key, self.value, self.filled_by)
1102
1113
 
1103
1114
 
1104
1115
  class Restarted(AlwaysEqualEventMixin):
@@ -11,6 +11,10 @@ from pypred import Predicate
11
11
 
12
12
  import rasa.shared.utils.io
13
13
  from rasa.shared.constants import RASA_DEFAULT_FLOW_PATTERN_PREFIX
14
+ from rasa.shared.core.constants import (
15
+ KEY_ASK_CONFIRM_DIGRESSIONS,
16
+ KEY_BLOCK_DIGRESSIONS,
17
+ )
14
18
  from rasa.shared.core.flows.flow_path import FlowPath, FlowPathsList, PathNode
15
19
  from rasa.shared.core.flows.flow_step import FlowStep
16
20
  from rasa.shared.core.flows.flow_step_links import (
@@ -33,6 +37,7 @@ from rasa.shared.core.flows.steps.constants import (
33
37
  START_STEP,
34
38
  )
35
39
  from rasa.shared.core.flows.steps.continuation import ContinueFlowStep
40
+ from rasa.shared.core.flows.utils import extract_digression_prop
36
41
  from rasa.shared.core.slots import Slot
37
42
 
38
43
  structlogger = structlog.get_logger()
@@ -62,6 +67,10 @@ class Flow:
62
67
  """The path to the file where the flow is stored."""
63
68
  persisted_slots: List[str] = field(default_factory=list)
64
69
  """The list of slots that should be persisted after the flow ends."""
70
+ ask_confirm_digressions: List[str] = field(default_factory=list)
71
+ """The flow ids for which the assistant should ask for confirmation."""
72
+ block_digressions: List[str] = field(default_factory=list)
73
+ """The flow ids that the assistant should block from digressing to."""
65
74
 
66
75
  @staticmethod
67
76
  def from_json(
@@ -98,6 +107,10 @@ class Flow:
98
107
  # data. When the model is trained, take the provided file_path.
99
108
  file_path=data.get("file_path") if "file_path" in data else file_path,
100
109
  persisted_slots=data.get("persisted_slots", []),
110
+ ask_confirm_digressions=extract_digression_prop(
111
+ KEY_ASK_CONFIRM_DIGRESSIONS, data
112
+ ),
113
+ block_digressions=extract_digression_prop(KEY_BLOCK_DIGRESSIONS, data),
101
114
  )
102
115
 
103
116
  def get_full_name(self) -> str:
@@ -172,6 +185,10 @@ class Flow:
172
185
  data["file_path"] = self.file_path
173
186
  if self.persisted_slots:
174
187
  data["persisted_slots"] = self.persisted_slots
188
+ if self.ask_confirm_digressions:
189
+ data[KEY_ASK_CONFIRM_DIGRESSIONS] = self.ask_confirm_digressions
190
+ if self.block_digressions:
191
+ data[KEY_BLOCK_DIGRESSIONS] = self.block_digressions
175
192
 
176
193
  return data
177
194
 
@@ -217,6 +217,12 @@
217
217
  "reset_after_flow_ends": {
218
218
  "type": "boolean"
219
219
  },
220
+ "ask_confirm_digressions": {
221
+ "$ref": "#/$defs/ask_confirm_digressions"
222
+ },
223
+ "block_digressions": {
224
+ "$ref": "#/$defs/block_digressions"
225
+ },
220
226
  "utter": {
221
227
  "type": "string"
222
228
  },
@@ -247,6 +253,32 @@
247
253
  }
248
254
  }
249
255
  },
256
+ "ask_confirm_digressions": {
257
+ "oneOf": [
258
+ {
259
+ "type": "boolean"
260
+ },
261
+ {
262
+ "type": "array",
263
+ "items": {
264
+ "type": "string"
265
+ }
266
+ }
267
+ ]
268
+ },
269
+ "block_digressions": {
270
+ "oneOf": [
271
+ {
272
+ "type": "boolean"
273
+ },
274
+ {
275
+ "type": "array",
276
+ "items": {
277
+ "type": "string"
278
+ }
279
+ }
280
+ ]
281
+ },
250
282
  "flow": {
251
283
  "required": [
252
284
  "steps",
@@ -282,6 +314,12 @@
282
314
  },
283
315
  "persisted_slots": {
284
316
  "$ref": "#/$defs/persisted_slots"
317
+ },
318
+ "ask_confirm_digressions": {
319
+ "$ref": "#/$defs/ask_confirm_digressions"
320
+ },
321
+ "block_digressions": {
322
+ "$ref": "#/$defs/block_digressions"
285
323
  }
286
324
  }
287
325
  },
@@ -1,10 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
- from dataclasses import dataclass
3
+ from dataclasses import dataclass, field
4
4
  from typing import Any, Dict, List, Set, Text
5
5
 
6
6
  from rasa.shared.constants import ACTION_ASK_PREFIX, UTTER_ASK_PREFIX
7
+ from rasa.shared.core.constants import (
8
+ KEY_ASK_CONFIRM_DIGRESSIONS,
9
+ KEY_BLOCK_DIGRESSIONS,
10
+ )
7
11
  from rasa.shared.core.flows.flow_step import FlowStep
12
+ from rasa.shared.core.flows.utils import extract_digression_prop
8
13
 
9
14
 
10
15
  @dataclass
@@ -59,6 +64,10 @@ class CollectInformationFlowStep(FlowStep):
59
64
  """Whether to always ask the question even if the slot is already filled."""
60
65
  reset_after_flow_ends: bool = True
61
66
  """Whether to reset the slot value at the end of the flow."""
67
+ ask_confirm_digressions: List[str] = field(default_factory=list)
68
+ """The flow id digressions for which the assistant should ask for confirmation."""
69
+ block_digressions: List[str] = field(default_factory=list)
70
+ """The flow id digressions that should be blocked during the flow step."""
62
71
 
63
72
  @classmethod
64
73
  def from_json(
@@ -86,6 +95,10 @@ class CollectInformationFlowStep(FlowStep):
86
95
  SlotRejection.from_dict(rejection)
87
96
  for rejection in data.get("rejections", [])
88
97
  ],
98
+ ask_confirm_digressions=extract_digression_prop(
99
+ KEY_ASK_CONFIRM_DIGRESSIONS, data
100
+ ),
101
+ block_digressions=extract_digression_prop(KEY_BLOCK_DIGRESSIONS, data),
89
102
  **base.__dict__,
90
103
  )
91
104
 
@@ -101,6 +114,10 @@ class CollectInformationFlowStep(FlowStep):
101
114
  data["ask_before_filling"] = self.ask_before_filling
102
115
  data["reset_after_flow_ends"] = self.reset_after_flow_ends
103
116
  data["rejections"] = [rejection.as_dict() for rejection in self.rejections]
117
+ data["ask_confirm_digressions"] = self.ask_confirm_digressions
118
+ data["block_digressions"] = (
119
+ self.block_digressions if self.block_digressions else False
120
+ )
104
121
 
105
122
  return data
106
123
 
@@ -1,9 +1,10 @@
1
- from typing import Set
1
+ from typing import Any, Dict, List, Set
2
2
 
3
3
  from rasa.shared.utils.io import raise_deprecation_warning
4
4
 
5
5
  RESET_PROPERTY_NAME = "reset_after_flow_ends"
6
6
  PERSIST_PROPERTY_NAME = "persisted_slots"
7
+ ALL_LABEL = "ALL"
7
8
 
8
9
 
9
10
  def warn_deprecated_collect_step_config(flow_id: str, collect_step: str) -> None:
@@ -38,3 +39,17 @@ def get_invalid_slot_persistence_config_error_message(
38
39
  f"are neither used in a collect step nor a set_slot step of the flow. "
39
40
  f"Please remove such slots from the '{PERSIST_PROPERTY_NAME}' property."
40
41
  )
42
+
43
+
44
+ def extract_digression_prop(prop: str, data: Dict[str, Any]) -> List[str]:
45
+ """Extracts the digression property from the data.
46
+
47
+ There can be two types of properties: ask_confirm_digressions and
48
+ block_digressions.
49
+ """
50
+ digression_property = data.get(prop, [])
51
+
52
+ if isinstance(digression_property, bool):
53
+ digression_property = [ALL_LABEL] if digression_property else []
54
+
55
+ return digression_property