rasa-pro 3.12.0.dev9__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 +20 -7
- rasa/core/actions/action_handle_digressions.py +142 -0
- rasa/core/actions/forms.py +10 -5
- rasa/core/channels/__init__.py +2 -0
- rasa/core/channels/voice_ready/audiocodes.py +42 -23
- 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/azure.py +2 -1
- 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/migrate.py +2 -2
- rasa/core/policies/flows/flow_executor.py +36 -42
- rasa/core/run.py +4 -3
- rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -2
- rasa/dialogue_understanding/commands/cancel_flow_command.py +62 -4
- rasa/dialogue_understanding/commands/change_flow_command.py +2 -2
- rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -2
- rasa/dialogue_understanding/commands/clarify_command.py +2 -2
- rasa/dialogue_understanding/commands/correct_slots_command.py +11 -2
- rasa/dialogue_understanding/commands/handle_digressions_command.py +150 -0
- rasa/dialogue_understanding/commands/human_handoff_command.py +2 -2
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -2
- rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -2
- rasa/dialogue_understanding/commands/set_slot_command.py +7 -15
- rasa/dialogue_understanding/commands/skip_question_command.py +2 -2
- rasa/dialogue_understanding/commands/start_flow_command.py +43 -2
- rasa/dialogue_understanding/commands/utils.py +1 -1
- rasa/dialogue_understanding/constants.py +1 -0
- rasa/dialogue_understanding/generator/command_generator.py +110 -73
- rasa/dialogue_understanding/generator/command_parser.py +1 -1
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +161 -3
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +10 -2
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +44 -3
- rasa/dialogue_understanding/generator/single_step/command_prompt_template.jinja2 +40 -40
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +11 -19
- rasa/dialogue_understanding/generator/utils.py +32 -1
- rasa/dialogue_understanding/patterns/correction.py +13 -1
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +62 -2
- rasa/dialogue_understanding/patterns/handle_digressions.py +81 -0
- rasa/dialogue_understanding/processor/command_processor.py +115 -28
- rasa/dialogue_understanding/utils.py +31 -0
- 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 +28 -3
- rasa/shared/core/domain.py +13 -20
- rasa/shared/core/events.py +13 -2
- rasa/shared/core/flows/flow.py +17 -0
- rasa/shared/core/flows/flows_yaml_schema.json +38 -0
- rasa/shared/core/flows/steps/collect.py +18 -1
- rasa/shared/core/flows/utils.py +16 -1
- rasa/shared/core/slot_mappings.py +144 -108
- rasa/shared/core/slots.py +23 -2
- rasa/shared/core/trackers.py +3 -1
- rasa/shared/nlu/constants.py +1 -0
- rasa/shared/providers/llm/_base_litellm_client.py +0 -40
- rasa/shared/utils/llm.py +1 -86
- rasa/shared/utils/schemas/domain.yml +0 -1
- rasa/telemetry.py +43 -13
- rasa/utils/common.py +0 -1
- rasa/validator.py +189 -82
- rasa/version.py +1 -1
- {rasa_pro-3.12.0.dev9.dist-info → rasa_pro-3.12.0.dev11.dist-info}/METADATA +1 -1
- {rasa_pro-3.12.0.dev9.dist-info → rasa_pro-3.12.0.dev11.dist-info}/RECORD +72 -68
- {rasa_pro-3.12.0.dev9.dist-info → rasa_pro-3.12.0.dev11.dist-info}/NOTICE +0 -0
- {rasa_pro-3.12.0.dev9.dist-info → rasa_pro-3.12.0.dev11.dist-info}/WHEEL +0 -0
- {rasa_pro-3.12.0.dev9.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,
|
|
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
|
|
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[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(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
|
@@ -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:
|
rasa/shared/core/trackers.py
CHANGED
|
@@ -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 "
|
rasa/shared/nlu/constants.py
CHANGED
|
@@ -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):
|