rasa-pro 3.12.0.dev10__py3-none-any.whl → 3.12.0.dev11__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.
- rasa/cli/inspect.py +20 -1
- rasa/cli/shell.py +3 -3
- rasa/core/actions/action.py +5 -6
- rasa/core/actions/forms.py +6 -3
- rasa/core/channels/__init__.py +2 -0
- rasa/core/channels/voice_stream/browser_audio.py +1 -0
- rasa/core/channels/voice_stream/call_state.py +7 -1
- rasa/core/channels/voice_stream/genesys.py +331 -0
- rasa/core/channels/voice_stream/tts/cartesia.py +16 -3
- rasa/core/channels/voice_stream/twilio_media_streams.py +2 -1
- rasa/core/channels/voice_stream/voice_channel.py +2 -1
- rasa/core/policies/flows/flow_executor.py +3 -41
- rasa/core/run.py +4 -3
- rasa/dialogue_understanding/generator/command_generator.py +104 -1
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +40 -6
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +1 -1
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +41 -2
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +1 -1
- rasa/dialogue_understanding/generator/utils.py +32 -1
- rasa/dialogue_understanding/processor/command_processor.py +10 -12
- rasa/dialogue_understanding_test/README.md +50 -0
- rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +3 -3
- rasa/model_service.py +4 -0
- rasa/model_training.py +24 -27
- rasa/shared/core/constants.py +6 -2
- rasa/shared/core/domain.py +11 -20
- rasa/shared/core/slot_mappings.py +143 -107
- rasa/shared/core/slots.py +4 -2
- rasa/telemetry.py +43 -13
- rasa/utils/common.py +0 -1
- rasa/validator.py +31 -74
- rasa/version.py +1 -1
- {rasa_pro-3.12.0.dev10.dist-info → rasa_pro-3.12.0.dev11.dist-info}/METADATA +1 -1
- {rasa_pro-3.12.0.dev10.dist-info → rasa_pro-3.12.0.dev11.dist-info}/RECORD +37 -36
- {rasa_pro-3.12.0.dev10.dist-info → rasa_pro-3.12.0.dev11.dist-info}/NOTICE +0 -0
- {rasa_pro-3.12.0.dev10.dist-info → rasa_pro-3.12.0.dev11.dist-info}/WHEEL +0 -0
- {rasa_pro-3.12.0.dev10.dist-info → rasa_pro-3.12.0.dev11.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,
|
|
9
14
|
KEY_MAPPING_TYPE,
|
|
15
|
+
KEY_RUN_ACTION_EVERY_TURN,
|
|
10
16
|
MAPPING_CONDITIONS,
|
|
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
|
|
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
|
|
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(
|
|
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"'{
|
|
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.
|
|
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
|
-
|
|
173
|
+
return mapping_type
|
|
174
|
+
|
|
90
175
|
def _get_active_loop_ignored_intents(
|
|
91
|
-
|
|
176
|
+
self, domain: "Domain", active_loop_name: Text
|
|
92
177
|
) -> List[Text]:
|
|
93
|
-
|
|
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.
|
|
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
|
-
|
|
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(
|
|
123
|
-
mapping_not_intents = SlotMapping.to_list(
|
|
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
|
-
+
|
|
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
|
-
|
|
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
|
-
|
|
181
|
-
and entity.get(
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
219
|
-
and
|
|
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 '{
|
|
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
|
-
|
|
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 '{
|
|
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
|
-
|
|
280
|
-
mapping: Dict[str, Any],
|
|
331
|
+
mapping: SlotMapping,
|
|
281
332
|
) -> bool:
|
|
282
333
|
"""Check if a slot mapping is valid."""
|
|
283
|
-
return
|
|
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:
|
|
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
|
|
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
|
-
|
|
301
|
-
) -> bool:
|
|
302
|
-
if mapping.get(MAPPING_CONDITIONS) and mapping[KEY_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
|
-
|
|
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:
|
|
356
|
+
self, mapping: SlotMapping, slot_name: Text
|
|
312
357
|
) -> bool:
|
|
313
|
-
slot_mapping_conditions = mapping.
|
|
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[
|
|
376
|
+
slot_mapping_conditions: List[SlotMappingCondition],
|
|
332
377
|
) -> bool:
|
|
333
378
|
active_flow_conditions = list(
|
|
334
|
-
filter(lambda x: x.
|
|
379
|
+
filter(lambda x: x.active_flow is not None, slot_mapping_conditions)
|
|
335
380
|
)
|
|
336
381
|
return any(
|
|
337
382
|
[
|
|
338
|
-
condition.
|
|
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[
|
|
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
|
-
|
|
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.
|
|
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:
|
|
416
|
+
mapping: SlotMapping,
|
|
374
417
|
) -> bool:
|
|
375
418
|
from rasa.core.actions.forms import FormAction
|
|
376
419
|
|
|
377
|
-
if mapping
|
|
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.
|
|
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:
|
|
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 =
|
|
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.
|
|
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,
|
|
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 =
|
|
498
|
-
mapping.get(KEY_MAPPING_TYPE, SlotMappingType.FROM_LLM.value)
|
|
499
|
-
)
|
|
535
|
+
mapping_type = mapping.type
|
|
500
536
|
|
|
501
|
-
if mapping_type in [SlotMappingType.FROM_LLM, SlotMappingType.
|
|
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,
|
|
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
|
@@ -60,8 +60,10 @@ class Slot(ABC):
|
|
|
60
60
|
dm1 or CALM finishes.
|
|
61
61
|
filled_by: The name of the extractor that fills the slot.
|
|
62
62
|
"""
|
|
63
|
+
from rasa.shared.core.slot_mappings import SlotMapping
|
|
64
|
+
|
|
63
65
|
self.name = name
|
|
64
|
-
self.mappings = mappings
|
|
66
|
+
self.mappings = [SlotMapping.from_dict(mapping, name) for mapping in mappings]
|
|
65
67
|
self._value = initial_value
|
|
66
68
|
self.initial_value = initial_value
|
|
67
69
|
self._value_reset_delay = value_reset_delay
|
|
@@ -193,7 +195,7 @@ class Slot(ABC):
|
|
|
193
195
|
"type": rasa.shared.utils.common.module_path_from_instance(self),
|
|
194
196
|
"initial_value": self.initial_value,
|
|
195
197
|
"influence_conversation": self.influence_conversation,
|
|
196
|
-
"mappings": self.mappings,
|
|
198
|
+
"mappings": [mapping.as_dict() for mapping in self.mappings],
|
|
197
199
|
}
|
|
198
200
|
|
|
199
201
|
def fingerprint(self) -> Text:
|
rasa/telemetry.py
CHANGED
|
@@ -15,7 +15,7 @@ from collections import defaultdict
|
|
|
15
15
|
from datetime import datetime
|
|
16
16
|
from functools import wraps
|
|
17
17
|
from pathlib import Path
|
|
18
|
-
from typing import Any, Callable, Dict, List, Optional, Text
|
|
18
|
+
from typing import Any, Callable, Dict, List, Optional, Text, Tuple
|
|
19
19
|
|
|
20
20
|
import importlib_resources
|
|
21
21
|
import requests
|
|
@@ -35,6 +35,7 @@ from rasa.constants import (
|
|
|
35
35
|
)
|
|
36
36
|
from rasa.engine.storage.local_model_storage import LocalModelStorage
|
|
37
37
|
from rasa.shared.constants import (
|
|
38
|
+
ASSISTANT_ID_KEY,
|
|
38
39
|
CONFIG_LANGUAGE_KEY,
|
|
39
40
|
CONFIG_PIPELINE_KEY,
|
|
40
41
|
CONFIG_POLICIES_KEY,
|
|
@@ -111,6 +112,7 @@ CI_ENVIRONMENT_TELL = [
|
|
|
111
112
|
# https://rasa.com/docs/rasa-pro/telemetry/reference
|
|
112
113
|
TRAINING_STARTED_EVENT = "Training Started"
|
|
113
114
|
TRAINING_COMPLETED_EVENT = "Training Completed"
|
|
115
|
+
TRAINING_FAILED_EVENT = "Training Failed"
|
|
114
116
|
TELEMETRY_DISABLED_EVENT = "Telemetry Disabled"
|
|
115
117
|
TELEMETRY_DATA_SPLIT_EVENT = "Training Data Split"
|
|
116
118
|
TELEMETRY_DATA_VALIDATED_EVENT = "Training Data Validated"
|
|
@@ -976,6 +978,7 @@ def track_model_training(
|
|
|
976
978
|
"language": config.get(CONFIG_LANGUAGE_KEY),
|
|
977
979
|
"training_id": training_id,
|
|
978
980
|
"type": model_type,
|
|
981
|
+
"assistant_id": config.get(ASSISTANT_ID_KEY),
|
|
979
982
|
"pipeline": config.get(CONFIG_PIPELINE_KEY),
|
|
980
983
|
"policies": config.get(CONFIG_POLICIES_KEY),
|
|
981
984
|
"train_schema": config.get(CONFIG_TRAIN_SCHEMA),
|
|
@@ -1021,13 +1024,28 @@ def track_model_training(
|
|
|
1021
1024
|
tracking_data,
|
|
1022
1025
|
)
|
|
1023
1026
|
start = datetime.now()
|
|
1024
|
-
|
|
1027
|
+
try:
|
|
1028
|
+
yield
|
|
1029
|
+
except (Exception, SystemExit):
|
|
1030
|
+
runtime = datetime.now() - start
|
|
1031
|
+
_track(
|
|
1032
|
+
TRAINING_FAILED_EVENT,
|
|
1033
|
+
{
|
|
1034
|
+
"training_id": training_id,
|
|
1035
|
+
"assistant_id": config.get(ASSISTANT_ID_KEY),
|
|
1036
|
+
"type": model_type,
|
|
1037
|
+
"runtime": int(runtime.total_seconds()),
|
|
1038
|
+
},
|
|
1039
|
+
)
|
|
1040
|
+
raise
|
|
1041
|
+
|
|
1025
1042
|
runtime = datetime.now() - start
|
|
1026
1043
|
|
|
1027
1044
|
_track(
|
|
1028
1045
|
TRAINING_COMPLETED_EVENT,
|
|
1029
1046
|
{
|
|
1030
1047
|
"training_id": training_id,
|
|
1048
|
+
"assistant_id": config.get(ASSISTANT_ID_KEY),
|
|
1031
1049
|
"type": model_type,
|
|
1032
1050
|
"runtime": int(runtime.total_seconds()),
|
|
1033
1051
|
},
|
|
@@ -1326,24 +1344,28 @@ def track_server_start(
|
|
|
1326
1344
|
"""
|
|
1327
1345
|
from rasa.core.utils import AvailableEndpoints
|
|
1328
1346
|
|
|
1329
|
-
def
|
|
1347
|
+
def project_fingerprint_and_assistant_id_from_model(
|
|
1330
1348
|
_model_directory: Optional[Text],
|
|
1331
|
-
) -> Optional[Text]:
|
|
1349
|
+
) -> Tuple[Optional[Text], Optional[Text]]:
|
|
1332
1350
|
"""Gets project fingerprint from an app's loaded model."""
|
|
1333
1351
|
if not model_directory:
|
|
1334
|
-
return None
|
|
1352
|
+
return None, None
|
|
1335
1353
|
|
|
1336
1354
|
try:
|
|
1337
1355
|
model_archive = model.get_local_model(_model_directory)
|
|
1338
1356
|
metadata = LocalModelStorage.metadata_from_archive(model_archive)
|
|
1339
1357
|
|
|
1340
|
-
return metadata.project_fingerprint
|
|
1358
|
+
return metadata.project_fingerprint, metadata.assistant_id
|
|
1341
1359
|
except Exception:
|
|
1342
|
-
return None
|
|
1360
|
+
return None, None
|
|
1343
1361
|
|
|
1344
1362
|
if not endpoints:
|
|
1345
1363
|
endpoints = AvailableEndpoints()
|
|
1346
1364
|
|
|
1365
|
+
project, assistant_id = project_fingerprint_and_assistant_id_from_model(
|
|
1366
|
+
model_directory
|
|
1367
|
+
)
|
|
1368
|
+
|
|
1347
1369
|
_track(
|
|
1348
1370
|
TELEMETRY_SERVER_STARTED_EVENT,
|
|
1349
1371
|
{
|
|
@@ -1365,7 +1387,8 @@ def track_server_start(
|
|
|
1365
1387
|
"endpoints_event_broker": endpoints.event_broker.type
|
|
1366
1388
|
if endpoints.event_broker
|
|
1367
1389
|
else None,
|
|
1368
|
-
"project":
|
|
1390
|
+
"project": project,
|
|
1391
|
+
"assistant_id": assistant_id,
|
|
1369
1392
|
},
|
|
1370
1393
|
)
|
|
1371
1394
|
|
|
@@ -1383,23 +1406,30 @@ def track_project_init(path: Text) -> None:
|
|
|
1383
1406
|
|
|
1384
1407
|
|
|
1385
1408
|
@ensure_telemetry_enabled
|
|
1386
|
-
def track_shell_started(model_type: Text) -> None:
|
|
1409
|
+
def track_shell_started(model_type: Text, assistant_id: Text) -> None:
|
|
1387
1410
|
"""Track when a user starts a bot using rasa shell.
|
|
1388
1411
|
|
|
1389
1412
|
Args:
|
|
1390
1413
|
model_type: Type of the model, core / nlu or rasa.
|
|
1391
1414
|
"""
|
|
1392
|
-
_track(
|
|
1415
|
+
_track(
|
|
1416
|
+
TELEMETRY_SHELL_STARTED_EVENT,
|
|
1417
|
+
{"type": model_type, "assistant_id": assistant_id},
|
|
1418
|
+
)
|
|
1393
1419
|
|
|
1394
1420
|
|
|
1395
1421
|
@ensure_telemetry_enabled
|
|
1396
|
-
def track_inspect_started(
|
|
1422
|
+
def track_inspect_started(channel: Text, assistant_id: Text) -> None:
|
|
1397
1423
|
"""Track when a user starts a bot using rasa inspect.
|
|
1398
1424
|
|
|
1399
1425
|
Args:
|
|
1400
|
-
|
|
1426
|
+
channel: Type of channel used.
|
|
1427
|
+
assistant_id: ID of the assistant being inspected.
|
|
1401
1428
|
"""
|
|
1402
|
-
_track(
|
|
1429
|
+
_track(
|
|
1430
|
+
TELEMETRY_INSPECT_STARTED_EVENT,
|
|
1431
|
+
{"type": channel, "assistant_id": assistant_id},
|
|
1432
|
+
)
|
|
1403
1433
|
|
|
1404
1434
|
|
|
1405
1435
|
@ensure_telemetry_enabled
|
rasa/utils/common.py
CHANGED
|
@@ -70,7 +70,6 @@ EXPECTED_WARNINGS: List[Tuple[Type[Warning], str]] = [
|
|
|
70
70
|
"Converting sparse IndexedSlices.* to a dense Tensor of unknown "
|
|
71
71
|
"shape. This may consume a large amount of memory.",
|
|
72
72
|
),
|
|
73
|
-
(UserWarning, "Slot auto-fill has been removed in 3.0 .*"),
|
|
74
73
|
# Cannot fix this deprecation warning since we need to support two
|
|
75
74
|
# numpy versions as long as we keep python 37 around
|
|
76
75
|
(DeprecationWarning, "the `interpolation=` argument to quantile was renamed"),
|