rasa-pro 3.12.0.dev9__py3-none-any.whl → 3.12.0.dev10__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 (56) hide show
  1. rasa/core/actions/action.py +17 -3
  2. rasa/core/actions/action_handle_digressions.py +142 -0
  3. rasa/core/actions/forms.py +4 -2
  4. rasa/core/channels/voice_ready/audiocodes.py +42 -23
  5. rasa/core/channels/voice_stream/tts/azure.py +2 -1
  6. rasa/core/migrate.py +2 -2
  7. rasa/core/policies/flows/flow_executor.py +33 -1
  8. rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -2
  9. rasa/dialogue_understanding/commands/cancel_flow_command.py +62 -4
  10. rasa/dialogue_understanding/commands/change_flow_command.py +2 -2
  11. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -2
  12. rasa/dialogue_understanding/commands/clarify_command.py +2 -2
  13. rasa/dialogue_understanding/commands/correct_slots_command.py +11 -2
  14. rasa/dialogue_understanding/commands/handle_digressions_command.py +150 -0
  15. rasa/dialogue_understanding/commands/human_handoff_command.py +2 -2
  16. rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -2
  17. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -2
  18. rasa/dialogue_understanding/commands/set_slot_command.py +7 -15
  19. rasa/dialogue_understanding/commands/skip_question_command.py +2 -2
  20. rasa/dialogue_understanding/commands/start_flow_command.py +43 -2
  21. rasa/dialogue_understanding/commands/utils.py +1 -1
  22. rasa/dialogue_understanding/constants.py +1 -0
  23. rasa/dialogue_understanding/generator/command_generator.py +10 -76
  24. rasa/dialogue_understanding/generator/command_parser.py +1 -1
  25. rasa/dialogue_understanding/generator/llm_based_command_generator.py +126 -2
  26. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +10 -2
  27. rasa/dialogue_understanding/generator/nlu_command_adapter.py +4 -2
  28. rasa/dialogue_understanding/generator/single_step/command_prompt_template.jinja2 +40 -40
  29. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +11 -19
  30. rasa/dialogue_understanding/patterns/correction.py +13 -1
  31. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +62 -2
  32. rasa/dialogue_understanding/patterns/handle_digressions.py +81 -0
  33. rasa/dialogue_understanding/processor/command_processor.py +117 -28
  34. rasa/dialogue_understanding/utils.py +31 -0
  35. rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +2 -2
  36. rasa/shared/core/constants.py +22 -1
  37. rasa/shared/core/domain.py +6 -4
  38. rasa/shared/core/events.py +13 -2
  39. rasa/shared/core/flows/flow.py +17 -0
  40. rasa/shared/core/flows/flows_yaml_schema.json +38 -0
  41. rasa/shared/core/flows/steps/collect.py +18 -1
  42. rasa/shared/core/flows/utils.py +16 -1
  43. rasa/shared/core/slot_mappings.py +6 -6
  44. rasa/shared/core/slots.py +19 -0
  45. rasa/shared/core/trackers.py +3 -1
  46. rasa/shared/nlu/constants.py +1 -0
  47. rasa/shared/providers/llm/_base_litellm_client.py +0 -40
  48. rasa/shared/utils/llm.py +1 -86
  49. rasa/shared/utils/schemas/domain.yml +0 -1
  50. rasa/validator.py +172 -22
  51. rasa/version.py +1 -1
  52. {rasa_pro-3.12.0.dev9.dist-info → rasa_pro-3.12.0.dev10.dist-info}/METADATA +1 -1
  53. {rasa_pro-3.12.0.dev9.dist-info → rasa_pro-3.12.0.dev10.dist-info}/RECORD +56 -53
  54. {rasa_pro-3.12.0.dev9.dist-info → rasa_pro-3.12.0.dev10.dist-info}/NOTICE +0 -0
  55. {rasa_pro-3.12.0.dev9.dist-info → rasa_pro-3.12.0.dev10.dist-info}/WHEEL +0 -0
  56. {rasa_pro-3.12.0.dev9.dist-info → rasa_pro-3.12.0.dev10.dist-info}/entry_points.txt +0 -0
@@ -20,7 +20,7 @@ from rasa.dialogue_understanding_test.test_case_simulation.exception import (
20
20
  )
21
21
  from rasa.dialogue_understanding_test.utils import filter_metadata
22
22
  from rasa.e2e_test.e2e_test_case import Fixture, Metadata
23
- from rasa.shared.core.constants import MAPPING_TYPE, SlotMappingType
23
+ from rasa.shared.core.constants import KEY_MAPPING_TYPE, SlotMappingType
24
24
  from rasa.shared.core.events import BotUttered, SlotSet, UserUttered
25
25
  from rasa.shared.core.trackers import DialogueStateTracker
26
26
  from rasa.shared.nlu.constants import COMMANDS, ENTITIES, INTENT
@@ -329,7 +329,7 @@ class TestCaseTrackerSimulator:
329
329
  # Use the SetSlotExtractor.NLU extractor if the slot mapping type is
330
330
  # not FROM_LLM.
331
331
  elif SlotMappingType.FROM_LLM.value not in [
332
- mapping[MAPPING_TYPE] for mapping in slot_definition.mappings
332
+ mapping[KEY_MAPPING_TYPE] for mapping in slot_definition.mappings
333
333
  ]:
334
334
  command.extractor = SetSlotExtractor.NLU.value
335
335
 
@@ -51,6 +51,8 @@ ACTION_TRIGGER_CHITCHAT = "action_trigger_chitchat"
51
51
  ACTION_RESET_ROUTING = "action_reset_routing"
52
52
  ACTION_HANGUP = "action_hangup"
53
53
  ACTION_REPEAT_BOT_MESSAGES = "action_repeat_bot_messages"
54
+ ACTION_BLOCK_DIGRESSION = "action_block_digression"
55
+ ACTION_CONTINUE_DIGRESSION = "action_continue_digression"
54
56
 
55
57
  ACTION_METADATA_EXECUTION_SUCCESS = "execution_success"
56
58
  ACTION_METADATA_EXECUTION_ERROR_MESSAGE = "execution_error_message"
@@ -81,6 +83,8 @@ DEFAULT_ACTION_NAMES = [
81
83
  ACTION_RESET_ROUTING,
82
84
  ACTION_HANGUP,
83
85
  ACTION_REPEAT_BOT_MESSAGES,
86
+ ACTION_BLOCK_DIGRESSION,
87
+ ACTION_CONTINUE_DIGRESSION,
84
88
  ]
85
89
 
86
90
  ACTION_SHOULD_SEND_DOMAIN = "send_domain"
@@ -137,7 +141,8 @@ DEFAULT_SLOT_NAMES = {
137
141
 
138
142
  SLOT_MAPPINGS = "mappings"
139
143
  MAPPING_CONDITIONS = "conditions"
140
- MAPPING_TYPE = "type"
144
+ KEY_MAPPING_TYPE = "type"
145
+ KEY_ALLOW_NLU_CORRECTION = "allow_nlu_correction"
141
146
 
142
147
 
143
148
  class SlotMappingType(Enum):
@@ -159,6 +164,18 @@ class SlotMappingType(Enum):
159
164
  return not (self == SlotMappingType.CUSTOM or self == SlotMappingType.FROM_LLM)
160
165
 
161
166
 
167
+ class SetSlotExtractor(Enum):
168
+ """The extractors that can set a slot."""
169
+
170
+ LLM = "LLM"
171
+ COMMAND_PAYLOAD_READER = "CommandPayloadReader"
172
+ NLU = "NLU"
173
+ CUSTOM = "CUSTOM"
174
+
175
+ def __str__(self) -> str:
176
+ return self.value
177
+
178
+
162
179
  # the keys for `State` (USER, PREVIOUS_ACTION, SLOTS, ACTIVE_LOOP)
163
180
  # represent the origin of a `SubState`
164
181
  USER = "user"
@@ -181,3 +198,7 @@ POLICY_NAME_RULE = "RulePolicy"
181
198
  CLASSIFIER_NAME_FALLBACK = "FallbackClassifier"
182
199
 
183
200
  POLICIES_THAT_EXTRACT_ENTITIES = {"TEDPolicy"}
201
+
202
+ # digression constants
203
+ KEY_ASK_CONFIRM_DIGRESSIONS = "ask_confirm_digressions"
204
+ KEY_BLOCK_DIGRESSIONS = "block_digressions"
@@ -47,9 +47,9 @@ from rasa.shared.constants import (
47
47
  from rasa.shared.core.constants import (
48
48
  ACTION_SHOULD_SEND_DOMAIN,
49
49
  ACTIVE_LOOP,
50
+ KEY_MAPPING_TYPE,
50
51
  KNOWLEDGE_BASE_SLOT_NAMES,
51
52
  MAPPING_CONDITIONS,
52
- MAPPING_TYPE,
53
53
  SLOT_MAPPINGS,
54
54
  SlotMappingType,
55
55
  )
@@ -596,7 +596,7 @@ class Domain:
596
596
  ),
597
597
  )
598
598
  slot_dict[slot_name][SLOT_MAPPINGS] = [
599
- {MAPPING_TYPE: SlotMappingType.FROM_LLM.value}
599
+ {KEY_MAPPING_TYPE: SlotMappingType.FROM_LLM.value}
600
600
  ]
601
601
 
602
602
  slot = slot_class(slot_name, **slot_dict[slot_name])
@@ -1570,7 +1570,9 @@ class Domain:
1570
1570
 
1571
1571
  for mapping in slot.mappings:
1572
1572
  mapping_conditions = mapping.get(MAPPING_CONDITIONS)
1573
- if mapping[MAPPING_TYPE] != str(SlotMappingType.FROM_ENTITY) or (
1573
+ if mapping[KEY_MAPPING_TYPE] != str(
1574
+ SlotMappingType.FROM_ENTITY
1575
+ ) or (
1574
1576
  mapping_conditions
1575
1577
  and mapping_conditions[0].get(ACTIVE_LOOP) is not None
1576
1578
  ):
@@ -2021,7 +2023,7 @@ class Domain:
2021
2023
  for slot in self.slots:
2022
2024
  total_mappings += len(slot.mappings)
2023
2025
  for mapping in slot.mappings:
2024
- if mapping[MAPPING_TYPE] == str(SlotMappingType.CUSTOM):
2026
+ if mapping[KEY_MAPPING_TYPE] == str(SlotMappingType.CUSTOM):
2025
2027
  custom_mappings += 1
2026
2028
 
2027
2029
  if MAPPING_CONDITIONS in mapping:
@@ -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
@@ -6,8 +6,8 @@ from rasa.shared.constants import DOCS_URL_NLU_BASED_SLOTS, IGNORED_INTENTS
6
6
  from rasa.shared.core.constants import (
7
7
  ACTIVE_FLOW,
8
8
  ACTIVE_LOOP,
9
+ KEY_MAPPING_TYPE,
9
10
  MAPPING_CONDITIONS,
10
- MAPPING_TYPE,
11
11
  REQUESTED_SLOT,
12
12
  SLOT_MAPPINGS,
13
13
  SlotMappingType,
@@ -59,11 +59,11 @@ class SlotMapping:
59
59
  )
60
60
 
61
61
  try:
62
- mapping_type = SlotMappingType(mapping.get(MAPPING_TYPE))
62
+ mapping_type = SlotMappingType(mapping.get(KEY_MAPPING_TYPE))
63
63
  except ValueError:
64
64
  raise InvalidDomain(
65
65
  f"Your domain uses an invalid slot mapping of type "
66
- f"'{mapping.get(MAPPING_TYPE)}' for slot '{slot_name}'. Please see "
66
+ f"'{mapping.get(KEY_MAPPING_TYPE)}' for slot '{slot_name}'. Please see "
67
67
  f"{DOCS_URL_NLU_BASED_SLOTS} for more information."
68
68
  )
69
69
 
@@ -299,7 +299,7 @@ class SlotFillingManager:
299
299
  def _verify_mapping_conditions(
300
300
  self, mapping: Dict[Text, Any], slot_name: Text
301
301
  ) -> bool:
302
- if mapping.get(MAPPING_CONDITIONS) and mapping[MAPPING_TYPE] != str(
302
+ if mapping.get(MAPPING_CONDITIONS) and mapping[KEY_MAPPING_TYPE] != str(
303
303
  SlotMappingType.FROM_TRIGGER_INTENT
304
304
  ):
305
305
  if not self._matches_mapping_conditions(mapping, slot_name):
@@ -374,7 +374,7 @@ class SlotFillingManager:
374
374
  ) -> bool:
375
375
  from rasa.core.actions.forms import FormAction
376
376
 
377
- if mapping[MAPPING_TYPE] != str(SlotMappingType.FROM_ENTITY):
377
+ if mapping[KEY_MAPPING_TYPE] != str(SlotMappingType.FROM_ENTITY):
378
378
  return False
379
379
 
380
380
  form_name = self.tracker.active_loop_name
@@ -495,7 +495,7 @@ def extract_slot_value(
495
495
 
496
496
  for mapping in slot.mappings:
497
497
  mapping_type = SlotMappingType(
498
- mapping.get(MAPPING_TYPE, SlotMappingType.FROM_LLM.value)
498
+ mapping.get(KEY_MAPPING_TYPE, SlotMappingType.FROM_LLM.value)
499
499
  )
500
500
 
501
501
  if mapping_type in [SlotMappingType.FROM_LLM, SlotMappingType.CUSTOM]:
rasa/shared/core/slots.py CHANGED
@@ -41,6 +41,7 @@ class Slot(ABC):
41
41
  influence_conversation: bool = True,
42
42
  is_builtin: bool = False,
43
43
  shared_for_coexistence: bool = False,
44
+ filled_by: Optional[str] = None,
44
45
  ) -> None:
45
46
  """Create a Slot.
46
47
 
@@ -57,6 +58,7 @@ class Slot(ABC):
57
58
  such as `return_value`.
58
59
  shared_for_coexistence: If `True` the slot is not forgotten after either
59
60
  dm1 or CALM finishes.
61
+ filled_by: The name of the extractor that fills the slot.
60
62
  """
61
63
  self.name = name
62
64
  self.mappings = mappings
@@ -67,6 +69,7 @@ class Slot(ABC):
67
69
  self._has_been_set = False
68
70
  self.is_builtin = is_builtin
69
71
  self.shared_for_coexistence = shared_for_coexistence
72
+ self._filled_by = filled_by
70
73
 
71
74
  def feature_dimensionality(self) -> int:
72
75
  """How many features this single slot creates.
@@ -132,6 +135,16 @@ class Slot(ABC):
132
135
  self._value = value
133
136
  self._has_been_set = True
134
137
 
138
+ @property
139
+ def filled_by(self) -> Optional[str]:
140
+ """Gets the slot's latest value extractor."""
141
+ return self._filled_by
142
+
143
+ @filled_by.setter
144
+ def filled_by(self, extractor: str) -> None:
145
+ """Sets the slot's latest value extractor."""
146
+ self._filled_by = extractor
147
+
135
148
  def has_same_coerced_value(self, other_value: Any) -> bool:
136
149
  """Checks if the coerced value of is the same as the slot value.
137
150
 
@@ -215,6 +228,7 @@ class FloatSlot(Slot):
215
228
  influence_conversation: bool = True,
216
229
  is_builtin: bool = False,
217
230
  shared_for_coexistence: bool = False,
231
+ filled_by: Optional[str] = None,
218
232
  ) -> None:
219
233
  """Creates a FloatSlot.
220
234
 
@@ -230,6 +244,7 @@ class FloatSlot(Slot):
230
244
  influence_conversation,
231
245
  is_builtin,
232
246
  shared_for_coexistence,
247
+ filled_by=filled_by,
233
248
  )
234
249
  self.max_value = max_value
235
250
  self.min_value = min_value
@@ -387,6 +402,7 @@ class CategoricalSlot(Slot):
387
402
  influence_conversation: bool = True,
388
403
  is_builtin: bool = False,
389
404
  shared_for_coexistence: bool = False,
405
+ filled_by: Optional[str] = None,
390
406
  ) -> None:
391
407
  """Creates a `Categorical Slot` (see parent class for detailed docstring)."""
392
408
  super().__init__(
@@ -397,6 +413,7 @@ class CategoricalSlot(Slot):
397
413
  influence_conversation,
398
414
  is_builtin,
399
415
  shared_for_coexistence,
416
+ filled_by=filled_by,
400
417
  )
401
418
  if values and None in values:
402
419
  rasa.shared.utils.io.raise_warning(
@@ -607,6 +624,7 @@ class AnySlot(Slot):
607
624
  influence_conversation: bool = False,
608
625
  is_builtin: bool = False,
609
626
  shared_for_coexistence: bool = False,
627
+ filled_by: Optional[str] = None,
610
628
  ) -> None:
611
629
  """Creates an `Any Slot` (see parent class for detailed docstring).
612
630
 
@@ -630,6 +648,7 @@ class AnySlot(Slot):
630
648
  influence_conversation,
631
649
  is_builtin,
632
650
  shared_for_coexistence,
651
+ filled_by=filled_by,
633
652
  )
634
653
 
635
654
  def __eq__(self, other: Any) -> bool:
@@ -916,11 +916,13 @@ class DialogueStateTracker:
916
916
  continue
917
917
  slot.reset()
918
918
 
919
- def _set_slot(self, key: Text, value: Any) -> None:
919
+ def _set_slot(self, key: Text, value: Any, filled_by: Optional[str] = None) -> None:
920
920
  """Sets the value of a slot if that slot exists."""
921
921
  if key in self.slots:
922
922
  slot = self.slots[key]
923
923
  slot.value = value
924
+ if filled_by is not None:
925
+ slot.filled_by = filled_by
924
926
  else:
925
927
  logger.error(
926
928
  f"Tried to set non existent slot '{key}'. Make sure you "
@@ -55,3 +55,4 @@ SPLIT_ENTITIES_BY_COMMA_DEFAULT_VALUE = True
55
55
  SINGLE_ENTITY_ALLOWED_INTERLEAVING_CHARSET = {".", ",", " ", ";"}
56
56
 
57
57
  SET_SLOT_COMMAND = "set slot"
58
+ HANDLE_DIGRESSIONS_COMMAND = "handle digressions"
@@ -181,46 +181,6 @@ class _BaseLiteLLMClient:
181
181
  )
182
182
  raise ProviderClientAPIException(e, message)
183
183
 
184
- @suppress_logs(log_level=logging.WARNING)
185
- async def acompletion_with_system(
186
- self, formatted_messages: Union[List[str], str]
187
- ) -> LLMResponse:
188
- """Asynchronously generate completions for given list of messages.
189
-
190
- Args:
191
- messages: List of messages or a single message to generate the
192
- completion for.
193
-
194
- Returns:
195
- List of message completions.
196
-
197
- Raises:
198
- ProviderClientAPIException: If the API request fails.
199
- """
200
- try:
201
- # formatted_messages = self._format_messages(messages)
202
- arguments = resolve_environment_variables(self._completion_fn_args)
203
- response = await acompletion(messages=formatted_messages, **arguments)
204
- return self._format_response(response)
205
- except Exception as e:
206
- message = ""
207
- from rasa.shared.providers.llm.self_hosted_llm_client import (
208
- SelfHostedLLMClient,
209
- )
210
-
211
- if isinstance(self, SelfHostedLLMClient):
212
- message = (
213
- "If you are using 'provider=self-hosted' to call a hosted vllm "
214
- "server make sure your config is correctly setup. You should have "
215
- "the following mandatory keys in your config: "
216
- "provider=self-hosted; "
217
- "model='<your-vllm-model-name>'; "
218
- "api_base='your-hosted-vllm-serv'."
219
- "In case you are getting OpenAI connection errors, such as missing "
220
- "API key, your configuration is incorrect."
221
- )
222
- raise ProviderClientAPIException(e, message)
223
-
224
184
  def _format_messages(self, messages: Union[List[str], str]) -> List[Dict[str, str]]:
225
185
  """Formats messages (or a single message) to OpenAI format."""
226
186
  if isinstance(messages, str):
rasa/shared/utils/llm.py CHANGED
@@ -6,7 +6,6 @@ from typing import (
6
6
  Any,
7
7
  Callable,
8
8
  Dict,
9
- List,
10
9
  Optional,
11
10
  Text,
12
11
  Type,
@@ -238,90 +237,6 @@ def tracker_as_readable_transcript(
238
237
  return "\n".join(transcript)
239
238
 
240
239
 
241
- def sanitize_command_for_prompt(cmd_dict):
242
- command = ""
243
- if cmd_dict["command"] == "start flow":
244
- command = f"start {cmd_dict['flow']}"
245
- elif cmd_dict["command"] == "set slot":
246
- command = f"set {cmd_dict['name']} {cmd_dict['value']}"
247
- elif cmd_dict["command"] == "skip question":
248
- command = "skip"
249
- elif cmd_dict["command"] == "clarify":
250
- command = f"clarify {' '.join(cmd_dict['options'])}"
251
- elif cmd_dict["command"] == "knowledge":
252
- command = "search"
253
- elif cmd_dict["command"] == "chitchat":
254
- command = "chat"
255
- elif cmd_dict["command"] == "cancel flow":
256
- command = "cancel"
257
- else:
258
- command = cmd_dict["command"]
259
-
260
- return command
261
-
262
-
263
- def tracker_as_message_list(
264
- tracker: "DialogueStateTracker",
265
- max_turns: Optional[int] = 20,
266
- ) -> List[str]:
267
- """Creates a readable dialogue from a tracker.
268
-
269
- Args:
270
- tracker: the tracker to convert
271
- max_turns: the maximum number of turns to include in the transcript
272
-
273
- Example:
274
- >>> tracker = Tracker(
275
- ... sender_id="test",
276
- ... slots=[],
277
- ... events=[
278
- ... UserUttered("hello"),
279
- ... BotUttered("hi"),
280
- ... ],
281
- ... )
282
- >>> tracker_as_readable_transcript(tracker)
283
- USER: hello
284
- AI: hi
285
-
286
- Returns:
287
- A string representing the transcript of the tracker
288
- """
289
- messages = []
290
-
291
- # using `applied_events` rather than `events` means that only events after the
292
- # most recent `Restart` or `SessionStarted` are included in the transcript
293
- # last_commands = None
294
- for event in tracker.applied_events():
295
- if isinstance(event, UserUttered):
296
- if event.has_triggered_error:
297
- first_error = event.error_commands[0]
298
- error_type = first_error.get("error_type")
299
- message = ERROR_PLACEHOLDER.get(
300
- error_type, ERROR_PLACEHOLDER["default"]
301
- )
302
- else:
303
- message = sanitize_message_for_prompt(event.text)
304
- # last_commands = event.commands
305
- messages.append({"role": "user", "content": message})
306
- # messages.append({"role": "system", "content": ' \n '.join([sanitize_command_for_prompt(cmd) for cmd in last_commands])}) # noqa: E501
307
- # transcript.append(f"{human_prefix}: {message}")
308
-
309
- elif isinstance(event, BotUttered):
310
- messages.append(
311
- {
312
- "role": "assistant",
313
- "content": f"{sanitize_message_for_prompt(event.text)}",
314
- }
315
- )
316
- # transcript.append(f"{ai_prefix}: {sanitize_message_for_prompt(event.text)}") # noqa: E501
317
-
318
- if max_turns:
319
- messages = messages[-max_turns:]
320
- # transcript = transcript[-max_turns:]
321
-
322
- return messages
323
-
324
-
325
240
  def sanitize_message_for_prompt(text: Optional[str]) -> str:
326
241
  """Removes new lines from a string.
327
242
 
@@ -765,7 +680,7 @@ def allowed_values_for_slot(slot: Slot) -> Union[str, None]:
765
680
  if isinstance(slot, BooleanSlot):
766
681
  return str([True, False])
767
682
  if isinstance(slot, CategoricalSlot):
768
- return str([v for v in slot.values if v != "__other__"] + ["other"])
683
+ return str([v for v in slot.values if v != "__other__"])
769
684
  else:
770
685
  return None
771
686
 
@@ -78,7 +78,6 @@ mapping:
78
78
  mappings:
79
79
  type: "seq"
80
80
  required: False
81
- allowempty: False
82
81
  sequence:
83
82
  - type: "map"
84
83
  allowempty: True