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
@@ -1,15 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+ import copy
1
4
  import logging
2
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Text, Tuple, cast
5
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Text, Tuple, Union, cast
6
+
7
+ from pydantic import BaseModel, Field
3
8
 
4
9
  import rasa.shared.utils.io
5
10
  from rasa.shared.constants import DOCS_URL_NLU_BASED_SLOTS, IGNORED_INTENTS
6
11
  from rasa.shared.core.constants import (
7
- ACTIVE_FLOW,
8
12
  ACTIVE_LOOP,
13
+ KEY_ACTION,
14
+ KEY_MAPPING_TYPE,
15
+ KEY_RUN_ACTION_EVERY_TURN,
9
16
  MAPPING_CONDITIONS,
10
- MAPPING_TYPE,
11
17
  REQUESTED_SLOT,
12
- SLOT_MAPPINGS,
13
18
  SlotMappingType,
14
19
  )
15
20
  from rasa.shared.core.slots import ListSlot, Slot
@@ -21,7 +26,6 @@ from rasa.shared.nlu.constants import (
21
26
  ENTITY_ATTRIBUTE_VALUE,
22
27
  INTENT,
23
28
  INTENT_NAME_KEY,
24
- NOT_INTENT,
25
29
  TEXT,
26
30
  )
27
31
 
@@ -35,11 +39,81 @@ if TYPE_CHECKING:
35
39
  logger = logging.getLogger(__name__)
36
40
 
37
41
 
38
- class SlotMapping:
42
+ class SlotMappingCondition(BaseModel):
43
+ """Defines a condition for a slot mapping."""
44
+
45
+ active_loop: Optional[str]
46
+ requested_slot: Optional[str] = None
47
+ active_flow: Optional[str] = None
48
+
49
+ @staticmethod
50
+ def from_dict(data: Dict[str, Any]) -> SlotMappingCondition:
51
+ # we allow None as a valid value for active_loop
52
+ # therefore we need to set a different default value
53
+ active_loop = data.pop(ACTIVE_LOOP, "")
54
+
55
+ return SlotMappingCondition(active_loop=active_loop, **data)
56
+
57
+ def as_dict(self) -> Dict[str, Any]:
58
+ return self.model_dump(exclude_none=True)
59
+
60
+
61
+ class SlotMapping(BaseModel):
39
62
  """Defines functionality for the available slot mappings."""
40
63
 
64
+ type: SlotMappingType
65
+ conditions: List[SlotMappingCondition] = Field(default_factory=list)
66
+ entity: Optional[str] = None
67
+ intent: Optional[Union[str, List[str]]] = None
68
+ role: Optional[str] = None
69
+ group: Optional[str] = None
70
+ not_intent: Optional[Union[str, List[str]]] = None
71
+ value: Optional[Any] = None
72
+ allow_nlu_correction: Optional[bool] = None
73
+ run_action_every_turn: Optional[str] = None
74
+
75
+ @staticmethod
76
+ def from_dict(data: Dict[str, Any], slot_name: str) -> SlotMapping:
77
+ data_copy = copy.deepcopy(data)
78
+ mapping_type = SlotMapping.validate_mapping(data_copy, slot_name)
79
+ conditions = [
80
+ SlotMappingCondition.from_dict(condition)
81
+ for condition in data_copy.pop(MAPPING_CONDITIONS, [])
82
+ ]
83
+
84
+ deprecated_action = data_copy.pop(KEY_ACTION, None)
85
+ if deprecated_action:
86
+ rasa.shared.utils.io.raise_deprecation_warning(
87
+ f"The `{KEY_ACTION}` key in slot mappings is deprecated and "
88
+ f"will be removed in Rasa Pro 4.0.0. "
89
+ f"Please use the `{KEY_RUN_ACTION_EVERY_TURN}` key instead.",
90
+ )
91
+ data_copy[KEY_RUN_ACTION_EVERY_TURN] = deprecated_action
92
+
93
+ run_action_every_turn = data_copy.pop(KEY_RUN_ACTION_EVERY_TURN, None)
94
+
95
+ return SlotMapping(
96
+ type=mapping_type,
97
+ conditions=conditions,
98
+ run_action_every_turn=run_action_every_turn,
99
+ **data_copy,
100
+ )
101
+
102
+ def as_dict(self) -> Dict[str, Any]:
103
+ data = self.model_dump(mode="json", exclude_none=True)
104
+ data[KEY_MAPPING_TYPE] = self.type.value
105
+
106
+ if self.conditions:
107
+ data[MAPPING_CONDITIONS] = [
108
+ condition.as_dict() for condition in self.conditions
109
+ ]
110
+ else:
111
+ data.pop(MAPPING_CONDITIONS, None)
112
+
113
+ return data
114
+
41
115
  @staticmethod
42
- def validate(mapping: Dict[Text, Any], slot_name: Text) -> None:
116
+ def validate_mapping(mapping: Dict[str, Any], slot_name: str) -> SlotMappingType:
43
117
  """Validates a slot mapping.
44
118
 
45
119
  Args:
@@ -58,12 +132,22 @@ class SlotMapping:
58
132
  f"{DOCS_URL_NLU_BASED_SLOTS} for more information."
59
133
  )
60
134
 
135
+ mapping_raw = mapping.pop(KEY_MAPPING_TYPE, SlotMappingType.FROM_LLM.value)
136
+
137
+ if mapping_raw == "custom":
138
+ rasa.shared.utils.io.raise_deprecation_warning(
139
+ "The `custom` slot mapping type is deprecated and "
140
+ "will be removed in Rasa Pro 4.0.0. "
141
+ "Please use the `controlled` slot mapping type instead.",
142
+ )
143
+ mapping_raw = "controlled"
144
+
61
145
  try:
62
- mapping_type = SlotMappingType(mapping.get(MAPPING_TYPE))
146
+ mapping_type = SlotMappingType(mapping_raw)
63
147
  except ValueError:
64
148
  raise InvalidDomain(
65
149
  f"Your domain uses an invalid slot mapping of type "
66
- f"'{mapping.get(MAPPING_TYPE)}' for slot '{slot_name}'. Please see "
150
+ f"'{mapping_raw}' for slot '{slot_name}'. Please see "
67
151
  f"{DOCS_URL_NLU_BASED_SLOTS} for more information."
68
152
  )
69
153
 
@@ -72,7 +156,7 @@ class SlotMapping:
72
156
  SlotMappingType.FROM_INTENT: ["value"],
73
157
  SlotMappingType.FROM_TRIGGER_INTENT: ["value"],
74
158
  SlotMappingType.FROM_TEXT: [],
75
- SlotMappingType.CUSTOM: [],
159
+ SlotMappingType.CONTROLLED: [],
76
160
  SlotMappingType.FROM_LLM: [],
77
161
  }
78
162
 
@@ -86,19 +170,18 @@ class SlotMapping:
86
170
  f"{DOCS_URL_NLU_BASED_SLOTS} for more information."
87
171
  )
88
172
 
89
- @staticmethod
173
+ return mapping_type
174
+
90
175
  def _get_active_loop_ignored_intents(
91
- mapping: Dict[Text, Any], domain: "Domain", active_loop_name: Text
176
+ self, domain: "Domain", active_loop_name: Text
92
177
  ) -> List[Text]:
93
- from rasa.shared.core.constants import ACTIVE_LOOP
94
-
95
- mapping_conditions = mapping.get(MAPPING_CONDITIONS)
178
+ mapping_conditions = self.conditions
96
179
  active_loop_match = True
97
180
  ignored_intents = []
98
181
 
99
182
  if mapping_conditions:
100
183
  match_list = [
101
- condition.get(ACTIVE_LOOP) == active_loop_name
184
+ condition.active_loop == active_loop_name
102
185
  for condition in mapping_conditions
103
186
  ]
104
187
  active_loop_match = any(match_list)
@@ -111,24 +194,21 @@ class SlotMapping:
111
194
 
112
195
  return ignored_intents
113
196
 
114
- @staticmethod
115
197
  def intent_is_desired(
116
- mapping: Dict[Text, Any],
198
+ self,
117
199
  tracker: "DialogueStateTracker",
118
200
  domain: "Domain",
119
201
  message: Optional["Message"] = None,
120
202
  ) -> bool:
121
203
  """Checks whether user intent matches slot mapping intent specifications."""
122
- mapping_intents = SlotMapping.to_list(mapping.get(INTENT, []))
123
- mapping_not_intents = SlotMapping.to_list(mapping.get(NOT_INTENT, []))
204
+ mapping_intents = SlotMapping.to_list(self.intent)
205
+ mapping_not_intents = SlotMapping.to_list(self.not_intent)
124
206
 
125
207
  active_loop_name = tracker.active_loop_name
126
208
  if active_loop_name:
127
209
  mapping_not_intents = (
128
210
  mapping_not_intents
129
- + SlotMapping._get_active_loop_ignored_intents(
130
- mapping, domain, active_loop_name
131
- )
211
+ + self._get_active_loop_ignored_intents(domain, active_loop_name)
132
212
  )
133
213
 
134
214
  if message is not None:
@@ -155,16 +235,14 @@ class SlotMapping:
155
235
 
156
236
  return x
157
237
 
158
- @staticmethod
159
238
  def entity_is_desired(
160
- mapping: Dict[Text, Any],
239
+ self,
161
240
  tracker: "DialogueStateTracker",
162
241
  message: Optional["Message"] = None,
163
242
  ) -> List[str]:
164
243
  """Checks whether slot should be filled by an entity in the input or not.
165
244
 
166
245
  Args:
167
- mapping: Slot mapping.
168
246
  tracker: The tracker.
169
247
  message: The message being processed.
170
248
 
@@ -176,19 +254,16 @@ class SlotMapping:
176
254
  matching_values = [
177
255
  cast(Text, entity[ENTITY_ATTRIBUTE_VALUE])
178
256
  for entity in extracted_entities
179
- if entity.get(ENTITY_ATTRIBUTE_TYPE)
180
- == mapping.get(ENTITY_ATTRIBUTE_TYPE)
181
- and entity.get(ENTITY_ATTRIBUTE_GROUP)
182
- == mapping.get(ENTITY_ATTRIBUTE_GROUP)
183
- and entity.get(ENTITY_ATTRIBUTE_ROLE)
184
- == mapping.get(ENTITY_ATTRIBUTE_ROLE)
257
+ if entity.get(ENTITY_ATTRIBUTE_TYPE) == self.entity
258
+ and entity.get(ENTITY_ATTRIBUTE_GROUP) == self.group
259
+ and entity.get(ENTITY_ATTRIBUTE_ROLE) == self.role
185
260
  ]
186
261
  elif tracker.latest_message and tracker.latest_message.text is not None:
187
262
  matching_values = list(
188
263
  tracker.get_latest_entity_values(
189
- mapping.get(ENTITY_ATTRIBUTE_TYPE),
190
- mapping.get(ENTITY_ATTRIBUTE_ROLE),
191
- mapping.get(ENTITY_ATTRIBUTE_GROUP),
264
+ self.entity,
265
+ self.role,
266
+ self.group,
192
267
  )
193
268
  )
194
269
  else:
@@ -196,45 +271,38 @@ class SlotMapping:
196
271
 
197
272
  return matching_values
198
273
 
199
- @staticmethod
200
274
  def check_mapping_validity(
275
+ self,
201
276
  slot_name: Text,
202
- mapping_type: SlotMappingType,
203
- mapping: Dict[Text, Any],
204
277
  domain: "Domain",
205
278
  ) -> bool:
206
279
  """Checks the mapping for validity.
207
280
 
208
281
  Args:
209
282
  slot_name: The name of the slot to be validated.
210
- mapping_type: The type of the slot mapping.
211
- mapping: Slot mapping.
212
283
  domain: The domain to check against.
213
284
 
214
285
  Returns:
215
286
  True, if intent and entity specified in a mapping exist in domain.
216
287
  """
217
288
  if (
218
- mapping_type == SlotMappingType.FROM_ENTITY
219
- and mapping.get(ENTITY_ATTRIBUTE_TYPE) not in domain.entities
289
+ self.type == SlotMappingType.FROM_ENTITY
290
+ and self.entity not in domain.entities
220
291
  ):
221
292
  rasa.shared.utils.io.raise_warning(
222
293
  f"Slot '{slot_name}' uses a 'from_entity' mapping "
223
- f"for a non-existent entity '{mapping.get(ENTITY_ATTRIBUTE_TYPE)}'. "
294
+ f"for a non-existent entity '{self.entity}'. "
224
295
  f"Skipping slot extraction because of invalid mapping."
225
296
  )
226
297
  return False
227
298
 
228
- if (
229
- mapping_type == SlotMappingType.FROM_INTENT
230
- and mapping.get(INTENT) is not None
231
- ):
232
- intent_list = SlotMapping.to_list(mapping.get(INTENT))
299
+ if self.type == SlotMappingType.FROM_INTENT and self.intent is not None:
300
+ intent_list = SlotMapping.to_list(self.intent)
233
301
  for intent in intent_list:
234
302
  if intent and intent not in domain.intents:
235
303
  rasa.shared.utils.io.raise_warning(
236
304
  f"Slot '{slot_name}' uses a 'from_intent' mapping for "
237
- f"a non-existent intent '{mapping.get('intent')}'. "
305
+ f"a non-existent intent '{intent}'. "
238
306
  f"Skipping slot extraction because of invalid mapping."
239
307
  )
240
308
  return False
@@ -242,22 +310,6 @@ class SlotMapping:
242
310
  return True
243
311
 
244
312
 
245
- def validate_slot_mappings(domain_slots: Dict[Text, Any]) -> None:
246
- """Raises InvalidDomain exception if slot mappings are invalid."""
247
- rasa.shared.utils.io.raise_warning(
248
- f"Slot auto-fill has been removed in 3.0 and replaced with a "
249
- f"new explicit mechanism to set slots. "
250
- f"Please refer to {DOCS_URL_NLU_BASED_SLOTS} to learn more.",
251
- UserWarning,
252
- )
253
-
254
- for slot_name, properties in domain_slots.items():
255
- mappings = properties.get(SLOT_MAPPINGS, [])
256
-
257
- for slot_mapping in mappings:
258
- SlotMapping.validate(slot_mapping, slot_name)
259
-
260
-
261
313
  class SlotFillingManager:
262
314
  """Manages slot filling based on conversation context."""
263
315
 
@@ -276,41 +328,34 @@ class SlotFillingManager:
276
328
  def is_slot_mapping_valid(
277
329
  self,
278
330
  slot_name: str,
279
- mapping_type: SlotMappingType,
280
- mapping: Dict[str, Any],
331
+ mapping: SlotMapping,
281
332
  ) -> bool:
282
333
  """Check if a slot mapping is valid."""
283
- return SlotMapping.check_mapping_validity(
334
+ return mapping.check_mapping_validity(
284
335
  slot_name=slot_name,
285
- mapping_type=mapping_type,
286
- mapping=mapping,
287
336
  domain=self.domain,
288
337
  )
289
338
 
290
- def is_intent_desired(self, mapping: Dict[str, Any]) -> bool:
339
+ def is_intent_desired(self, mapping: SlotMapping) -> bool:
291
340
  """Check if the intent matches the one indicated in the slot mapping."""
292
- return SlotMapping.intent_is_desired(
293
- mapping=mapping,
341
+ return mapping.intent_is_desired(
294
342
  tracker=self.tracker,
295
343
  domain=self.domain,
296
344
  message=self.message,
297
345
  )
298
346
 
299
- def _verify_mapping_conditions(
300
- self, mapping: Dict[Text, Any], slot_name: Text
301
- ) -> bool:
302
- if mapping.get(MAPPING_CONDITIONS) and mapping[MAPPING_TYPE] != str(
347
+ def _verify_mapping_conditions(self, mapping: SlotMapping, slot_name: Text) -> bool:
348
+ if mapping.conditions and mapping.type != str(
303
349
  SlotMappingType.FROM_TRIGGER_INTENT
304
350
  ):
305
- if not self._matches_mapping_conditions(mapping, slot_name):
306
- return False
351
+ return self._matches_mapping_conditions(mapping, slot_name)
307
352
 
308
353
  return True
309
354
 
310
355
  def _matches_mapping_conditions(
311
- self, mapping: Dict[Text, Any], slot_name: Text
356
+ self, mapping: SlotMapping, slot_name: Text
312
357
  ) -> bool:
313
- slot_mapping_conditions = mapping.get(MAPPING_CONDITIONS)
358
+ slot_mapping_conditions = mapping.conditions
314
359
 
315
360
  if not slot_mapping_conditions:
316
361
  return True
@@ -328,20 +373,20 @@ class SlotFillingManager:
328
373
  @staticmethod
329
374
  def _mapping_conditions_match_flow(
330
375
  active_flow: str,
331
- slot_mapping_conditions: List[Dict[str, str]],
376
+ slot_mapping_conditions: List[SlotMappingCondition],
332
377
  ) -> bool:
333
378
  active_flow_conditions = list(
334
- filter(lambda x: x.get(ACTIVE_FLOW) is not None, slot_mapping_conditions)
379
+ filter(lambda x: x.active_flow is not None, slot_mapping_conditions)
335
380
  )
336
381
  return any(
337
382
  [
338
- condition.get(ACTIVE_FLOW) == active_flow
383
+ condition.active_flow == active_flow
339
384
  for condition in active_flow_conditions
340
385
  ]
341
386
  )
342
387
 
343
388
  def _mapping_conditions_match_form(
344
- self, slot_name: str, slot_mapping_conditions: List[Dict[str, str]]
389
+ self, slot_name: str, slot_mapping_conditions: List[SlotMappingCondition]
345
390
  ) -> bool:
346
391
  if (
347
392
  self.tracker.is_active_loop_rejected
@@ -351,12 +396,10 @@ class SlotFillingManager:
351
396
 
352
397
  # check if found mapping conditions matches form
353
398
  for condition in slot_mapping_conditions:
354
- # we allow None as a valid value for active_loop
355
- # therefore we need to set a different default value
356
- active_loop = condition.get(ACTIVE_LOOP, "")
399
+ active_loop = condition.active_loop
357
400
 
358
401
  if active_loop and active_loop == self.tracker.active_loop_name:
359
- condition_requested_slot = condition.get(REQUESTED_SLOT)
402
+ condition_requested_slot = condition.requested_slot
360
403
  if not condition_requested_slot:
361
404
  return True
362
405
  if condition_requested_slot == self.tracker.get_slot(REQUESTED_SLOT):
@@ -370,11 +413,11 @@ class SlotFillingManager:
370
413
  def _fails_unique_entity_mapping_check(
371
414
  self,
372
415
  slot_name: Text,
373
- mapping: Dict[Text, Any],
416
+ mapping: SlotMapping,
374
417
  ) -> bool:
375
418
  from rasa.core.actions.forms import FormAction
376
419
 
377
- if mapping[MAPPING_TYPE] != str(SlotMappingType.FROM_ENTITY):
420
+ if mapping.type != SlotMappingType.FROM_ENTITY:
378
421
  return False
379
422
 
380
423
  form_name = self.tracker.active_loop_name
@@ -395,12 +438,9 @@ class SlotFillingManager:
395
438
 
396
439
  return True
397
440
 
398
- def _is_trigger_intent_mapping_condition_met(
399
- self, mapping: Dict[Text, Any]
400
- ) -> bool:
441
+ def _is_trigger_intent_mapping_condition_met(self, mapping: SlotMapping) -> bool:
401
442
  active_loops_in_mapping_conditions = [
402
- condition.get(ACTIVE_LOOP)
403
- for condition in mapping.get(MAPPING_CONDITIONS, [])
443
+ condition.active_loop for condition in mapping.conditions
404
444
  ]
405
445
 
406
446
  trigger_mapping_condition_met = True
@@ -421,7 +461,7 @@ class SlotFillingManager:
421
461
  def extract_slot_value_from_predefined_mapping(
422
462
  self,
423
463
  mapping_type: SlotMappingType,
424
- mapping: Dict[Text, Any],
464
+ mapping: SlotMapping,
425
465
  ) -> List[Any]:
426
466
  """Extracts slot value if slot has an applicable predefined mapping."""
427
467
  if (
@@ -454,9 +494,9 @@ class SlotFillingManager:
454
494
  value: List[Any] = []
455
495
 
456
496
  if should_fill_entity_slot:
457
- value = SlotMapping.entity_is_desired(mapping, self.tracker, self.message)
497
+ value = mapping.entity_is_desired(self.tracker, self.message)
458
498
  elif should_fill_intent_slot or should_fill_trigger_slot:
459
- value = [mapping.get("value")]
499
+ value = [mapping.value]
460
500
  elif should_fill_text_slot:
461
501
  value = [self.message.get(TEXT)] if self.message is not None else []
462
502
  if not value:
@@ -468,11 +508,9 @@ class SlotFillingManager:
468
508
 
469
509
  return value
470
510
 
471
- def should_fill_slot(
472
- self, slot_name: str, mapping_type: SlotMappingType, mapping: Dict[Text, Any]
473
- ) -> bool:
511
+ def should_fill_slot(self, slot_name: str, mapping: SlotMapping) -> bool:
474
512
  """Checks if a slot should be filled based on the conversation context."""
475
- if not self.is_slot_mapping_valid(slot_name, mapping_type, mapping):
513
+ if not self.is_slot_mapping_valid(slot_name, mapping):
476
514
  return False
477
515
 
478
516
  if not self.is_intent_desired(mapping):
@@ -494,14 +532,12 @@ def extract_slot_value(
494
532
  is_extracted = False
495
533
 
496
534
  for mapping in slot.mappings:
497
- mapping_type = SlotMappingType(
498
- mapping.get(MAPPING_TYPE, SlotMappingType.FROM_LLM.value)
499
- )
535
+ mapping_type = mapping.type
500
536
 
501
- if mapping_type in [SlotMappingType.FROM_LLM, SlotMappingType.CUSTOM]:
537
+ if mapping_type in [SlotMappingType.FROM_LLM, SlotMappingType.CONTROLLED]:
502
538
  continue
503
539
 
504
- if not slot_filling_manager.should_fill_slot(slot.name, mapping_type, mapping):
540
+ if not slot_filling_manager.should_fill_slot(slot.name, mapping):
505
541
  continue
506
542
 
507
543
  value: List[Any] = (
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,9 +58,12 @@ 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
  """
63
+ from rasa.shared.core.slot_mappings import SlotMapping
64
+
61
65
  self.name = name
62
- self.mappings = mappings
66
+ self.mappings = [SlotMapping.from_dict(mapping, name) for mapping in mappings]
63
67
  self._value = initial_value
64
68
  self.initial_value = initial_value
65
69
  self._value_reset_delay = value_reset_delay
@@ -67,6 +71,7 @@ class Slot(ABC):
67
71
  self._has_been_set = False
68
72
  self.is_builtin = is_builtin
69
73
  self.shared_for_coexistence = shared_for_coexistence
74
+ self._filled_by = filled_by
70
75
 
71
76
  def feature_dimensionality(self) -> int:
72
77
  """How many features this single slot creates.
@@ -132,6 +137,16 @@ class Slot(ABC):
132
137
  self._value = value
133
138
  self._has_been_set = True
134
139
 
140
+ @property
141
+ def filled_by(self) -> Optional[str]:
142
+ """Gets the slot's latest value extractor."""
143
+ return self._filled_by
144
+
145
+ @filled_by.setter
146
+ def filled_by(self, extractor: str) -> None:
147
+ """Sets the slot's latest value extractor."""
148
+ self._filled_by = extractor
149
+
135
150
  def has_same_coerced_value(self, other_value: Any) -> bool:
136
151
  """Checks if the coerced value of is the same as the slot value.
137
152
 
@@ -180,7 +195,7 @@ class Slot(ABC):
180
195
  "type": rasa.shared.utils.common.module_path_from_instance(self),
181
196
  "initial_value": self.initial_value,
182
197
  "influence_conversation": self.influence_conversation,
183
- "mappings": self.mappings,
198
+ "mappings": [mapping.as_dict() for mapping in self.mappings],
184
199
  }
185
200
 
186
201
  def fingerprint(self) -> Text:
@@ -215,6 +230,7 @@ class FloatSlot(Slot):
215
230
  influence_conversation: bool = True,
216
231
  is_builtin: bool = False,
217
232
  shared_for_coexistence: bool = False,
233
+ filled_by: Optional[str] = None,
218
234
  ) -> None:
219
235
  """Creates a FloatSlot.
220
236
 
@@ -230,6 +246,7 @@ class FloatSlot(Slot):
230
246
  influence_conversation,
231
247
  is_builtin,
232
248
  shared_for_coexistence,
249
+ filled_by=filled_by,
233
250
  )
234
251
  self.max_value = max_value
235
252
  self.min_value = min_value
@@ -387,6 +404,7 @@ class CategoricalSlot(Slot):
387
404
  influence_conversation: bool = True,
388
405
  is_builtin: bool = False,
389
406
  shared_for_coexistence: bool = False,
407
+ filled_by: Optional[str] = None,
390
408
  ) -> None:
391
409
  """Creates a `Categorical Slot` (see parent class for detailed docstring)."""
392
410
  super().__init__(
@@ -397,6 +415,7 @@ class CategoricalSlot(Slot):
397
415
  influence_conversation,
398
416
  is_builtin,
399
417
  shared_for_coexistence,
418
+ filled_by=filled_by,
400
419
  )
401
420
  if values and None in values:
402
421
  rasa.shared.utils.io.raise_warning(
@@ -607,6 +626,7 @@ class AnySlot(Slot):
607
626
  influence_conversation: bool = False,
608
627
  is_builtin: bool = False,
609
628
  shared_for_coexistence: bool = False,
629
+ filled_by: Optional[str] = None,
610
630
  ) -> None:
611
631
  """Creates an `Any Slot` (see parent class for detailed docstring).
612
632
 
@@ -630,6 +650,7 @@ class AnySlot(Slot):
630
650
  influence_conversation,
631
651
  is_builtin,
632
652
  shared_for_coexistence,
653
+ filled_by=filled_by,
633
654
  )
634
655
 
635
656
  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"
rasa/shared/utils/llm.py CHANGED
@@ -680,7 +680,7 @@ def allowed_values_for_slot(slot: Slot) -> Union[str, None]:
680
680
  if isinstance(slot, BooleanSlot):
681
681
  return str([True, False])
682
682
  if isinstance(slot, CategoricalSlot):
683
- return str([v for v in slot.values if v != "__other__"] + ["other"])
683
+ return str([v for v in slot.values if v != "__other__"])
684
684
  else:
685
685
  return None
686
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