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,11 +1,13 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from typing import Any, Dict, List, Optional, Text
|
|
2
|
+
from typing import Any, Dict, List, Optional, Set, Text, Tuple
|
|
3
3
|
|
|
4
4
|
import structlog
|
|
5
5
|
|
|
6
6
|
from rasa.dialogue_understanding.commands import (
|
|
7
7
|
Command,
|
|
8
|
+
CorrectSlotsCommand,
|
|
8
9
|
ErrorCommand,
|
|
10
|
+
SetSlotCommand,
|
|
9
11
|
StartFlowCommand,
|
|
10
12
|
)
|
|
11
13
|
from rasa.dialogue_understanding.utils import (
|
|
@@ -198,6 +200,94 @@ class CommandGenerator:
|
|
|
198
200
|
"""
|
|
199
201
|
raise NotImplementedError()
|
|
200
202
|
|
|
203
|
+
def _check_commands_overlap(
|
|
204
|
+
self, prior_commands: List[Command], commands: List[Command]
|
|
205
|
+
) -> List[Command]:
|
|
206
|
+
"""Check if there is overlap between the prior commands and the current ones.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
prior_commands: The prior commands.
|
|
210
|
+
commands: The commands to check.
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
The final commands.
|
|
214
|
+
"""
|
|
215
|
+
if not prior_commands:
|
|
216
|
+
return commands
|
|
217
|
+
|
|
218
|
+
prior_commands, commands = self._check_slot_command_overlap(
|
|
219
|
+
prior_commands, commands
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
prior_start_flow_names = {
|
|
223
|
+
command.flow
|
|
224
|
+
for command in prior_commands
|
|
225
|
+
if isinstance(command, StartFlowCommand)
|
|
226
|
+
}
|
|
227
|
+
current_start_flow_names = {
|
|
228
|
+
command.flow
|
|
229
|
+
for command in commands
|
|
230
|
+
if isinstance(command, StartFlowCommand)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return self._check_start_flow_command_overlap(
|
|
234
|
+
prior_commands,
|
|
235
|
+
commands,
|
|
236
|
+
prior_start_flow_names,
|
|
237
|
+
current_start_flow_names,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
def _check_start_flow_command_overlap(
|
|
241
|
+
self,
|
|
242
|
+
prior_commands: List[Command],
|
|
243
|
+
commands: List[Command],
|
|
244
|
+
prior_start_flow_names: Set[str],
|
|
245
|
+
current_start_flow_names: Set[str],
|
|
246
|
+
) -> List[Command]:
|
|
247
|
+
"""Get the final commands.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
prior_commands: The prior commands.
|
|
251
|
+
commands: The currently predicted commands to check.
|
|
252
|
+
prior_start_flow_names: The names of the flows from the prior commands.
|
|
253
|
+
current_start_flow_names: The names of the flows from the current commands.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
The final commands.
|
|
257
|
+
"""
|
|
258
|
+
raise NotImplementedError()
|
|
259
|
+
|
|
260
|
+
def _check_slot_command_overlap(
|
|
261
|
+
self,
|
|
262
|
+
prior_commands: List[Command],
|
|
263
|
+
commands: List[Command],
|
|
264
|
+
) -> Tuple[List[Command], List[Command]]:
|
|
265
|
+
"""Check if the current commands overlap with the prior commands."""
|
|
266
|
+
prior_slot_names = gather_slot_names(prior_commands)
|
|
267
|
+
current_slot_names = gather_slot_names(commands)
|
|
268
|
+
overlapping_slot_names = prior_slot_names.intersection(current_slot_names)
|
|
269
|
+
|
|
270
|
+
structlogger.debug(
|
|
271
|
+
"command_generator.check_slot_command_overlap",
|
|
272
|
+
overlapping_slot_names=overlapping_slot_names,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
if not overlapping_slot_names:
|
|
276
|
+
return prior_commands, commands
|
|
277
|
+
|
|
278
|
+
return self._filter_slot_commands(
|
|
279
|
+
prior_commands, commands, overlapping_slot_names
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
def _filter_slot_commands(
|
|
283
|
+
self,
|
|
284
|
+
prior_commands: List[Command],
|
|
285
|
+
commands: List[Command],
|
|
286
|
+
overlapping_slot_names: Set[str],
|
|
287
|
+
) -> Tuple[List[Command], List[Command]]:
|
|
288
|
+
"""Filter out the overlapping slot commands."""
|
|
289
|
+
raise NotImplementedError()
|
|
290
|
+
|
|
201
291
|
def _check_commands_against_startable_flows(
|
|
202
292
|
self, commands: List[Command], startable_flows: FlowsList
|
|
203
293
|
) -> List[Command]:
|
|
@@ -279,3 +369,16 @@ class CommandGenerator:
|
|
|
279
369
|
return [
|
|
280
370
|
Command.command_from_json(command) for command in message.get(COMMANDS, [])
|
|
281
371
|
]
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def gather_slot_names(commands: List[Command]) -> Set[str]:
|
|
375
|
+
"""Gather all slot names from the commands."""
|
|
376
|
+
slot_names = set()
|
|
377
|
+
for command in commands:
|
|
378
|
+
if isinstance(command, SetSlotCommand):
|
|
379
|
+
slot_names.add(command.name)
|
|
380
|
+
if isinstance(command, CorrectSlotsCommand):
|
|
381
|
+
for slot in command.corrected_slots:
|
|
382
|
+
slot_names.add(slot.name)
|
|
383
|
+
|
|
384
|
+
return slot_names
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from functools import lru_cache
|
|
3
|
-
from typing import Any, Dict, List, Optional, Text, Tuple, Union
|
|
3
|
+
from typing import Any, Dict, List, Optional, Set, Text, Tuple, Union
|
|
4
4
|
|
|
5
5
|
import structlog
|
|
6
6
|
from jinja2 import Template
|
|
7
7
|
|
|
8
|
+
import rasa.dialogue_understanding.generator.utils
|
|
8
9
|
import rasa.shared.utils.io
|
|
9
10
|
from rasa.dialogue_understanding.commands import (
|
|
10
11
|
Command,
|
|
@@ -27,9 +28,7 @@ from rasa.engine.recipes.default_recipe import DefaultV1Recipe
|
|
|
27
28
|
from rasa.engine.storage.resource import Resource
|
|
28
29
|
from rasa.engine.storage.storage import ModelStorage
|
|
29
30
|
from rasa.shared.core.constants import (
|
|
30
|
-
KEY_MAPPING_TYPE,
|
|
31
31
|
SetSlotExtractor,
|
|
32
|
-
SlotMappingType,
|
|
33
32
|
)
|
|
34
33
|
from rasa.shared.core.domain import Domain
|
|
35
34
|
from rasa.shared.core.flows import Flow, FlowsList, FlowStep
|
|
@@ -547,11 +546,8 @@ class LLMBasedCommandGenerator(
|
|
|
547
546
|
for slot in llm_fillable_slots:
|
|
548
547
|
should_fill_slot = False
|
|
549
548
|
for mapping in slot.mappings: # type: ignore[union-attr]
|
|
550
|
-
mapping_type = SlotMappingType(mapping.get(KEY_MAPPING_TYPE))
|
|
551
|
-
|
|
552
549
|
should_fill_slot = slot_filling_manager.should_fill_slot(
|
|
553
550
|
slot.name, # type: ignore[union-attr]
|
|
554
|
-
mapping_type,
|
|
555
551
|
mapping,
|
|
556
552
|
)
|
|
557
553
|
|
|
@@ -578,3 +574,41 @@ class LLMBasedCommandGenerator(
|
|
|
578
574
|
]
|
|
579
575
|
|
|
580
576
|
return filtered_commands
|
|
577
|
+
|
|
578
|
+
def _check_start_flow_command_overlap(
|
|
579
|
+
self,
|
|
580
|
+
prior_commands: List[Command],
|
|
581
|
+
commands: List[Command],
|
|
582
|
+
prior_start_flow_names: Set[str],
|
|
583
|
+
current_start_flow_names: Set[str],
|
|
584
|
+
) -> List[Command]:
|
|
585
|
+
"""Prioritize the prior commands over the LLM-issued commands."""
|
|
586
|
+
different_flow_names = current_start_flow_names.difference(
|
|
587
|
+
prior_start_flow_names
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
if not different_flow_names:
|
|
591
|
+
return prior_commands + commands
|
|
592
|
+
|
|
593
|
+
# discard the flow names that are different to prior start flow commands
|
|
594
|
+
filtered_commands = [
|
|
595
|
+
command
|
|
596
|
+
for command in commands
|
|
597
|
+
if not isinstance(command, StartFlowCommand)
|
|
598
|
+
or command.flow not in different_flow_names
|
|
599
|
+
]
|
|
600
|
+
return prior_commands + filtered_commands
|
|
601
|
+
|
|
602
|
+
def _filter_slot_commands(
|
|
603
|
+
self,
|
|
604
|
+
prior_commands: List[Command],
|
|
605
|
+
commands: List[Command],
|
|
606
|
+
overlapping_slot_names: Set[str],
|
|
607
|
+
) -> Tuple[List[Command], List[Command]]:
|
|
608
|
+
"""Prioritize prior commands over LLM ones in the case of same slot."""
|
|
609
|
+
filtered_commands = (
|
|
610
|
+
rasa.dialogue_understanding.generator.utils.filter_slot_commands(
|
|
611
|
+
commands, overlapping_slot_names
|
|
612
|
+
)
|
|
613
|
+
)
|
|
614
|
+
return prior_commands, filtered_commands
|
|
@@ -229,7 +229,7 @@ class MultiStepLLMCommandGenerator(LLMBasedCommandGenerator):
|
|
|
229
229
|
domain = kwargs.get("domain")
|
|
230
230
|
commands = self._check_commands_against_slot_mappings(commands, tracker, domain)
|
|
231
231
|
|
|
232
|
-
return prior_commands
|
|
232
|
+
return self._check_commands_overlap(prior_commands, commands)
|
|
233
233
|
|
|
234
234
|
@classmethod
|
|
235
235
|
def parse_commands(
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
from typing import Any, Dict, List, Optional, Text
|
|
1
|
+
from typing import Any, Dict, List, Optional, Set, Text, Tuple
|
|
2
2
|
|
|
3
3
|
import structlog
|
|
4
4
|
|
|
5
|
+
import rasa.dialogue_understanding.generator.utils
|
|
5
6
|
from rasa.dialogue_understanding.commands import (
|
|
6
7
|
Command,
|
|
7
8
|
SetSlotCommand,
|
|
@@ -148,7 +149,7 @@ class NLUCommandAdapter(GraphComponent, CommandGenerator):
|
|
|
148
149
|
commands=commands,
|
|
149
150
|
)
|
|
150
151
|
|
|
151
|
-
return prior_commands
|
|
152
|
+
return self._check_commands_overlap(prior_commands, commands)
|
|
152
153
|
|
|
153
154
|
@staticmethod
|
|
154
155
|
def convert_nlu_to_commands(
|
|
@@ -210,6 +211,44 @@ class NLUCommandAdapter(GraphComponent, CommandGenerator):
|
|
|
210
211
|
)
|
|
211
212
|
return commands
|
|
212
213
|
|
|
214
|
+
def _check_start_flow_command_overlap(
|
|
215
|
+
self,
|
|
216
|
+
prior_commands: List[Command],
|
|
217
|
+
commands: List[Command],
|
|
218
|
+
prior_start_flow_names: Set[str],
|
|
219
|
+
current_start_flow_names: Set[str],
|
|
220
|
+
) -> List[Command]:
|
|
221
|
+
"""Prioritize the current NLU commands over the prior commands."""
|
|
222
|
+
different_flow_names = prior_start_flow_names.difference(
|
|
223
|
+
current_start_flow_names
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
if not different_flow_names:
|
|
227
|
+
return prior_commands + commands
|
|
228
|
+
|
|
229
|
+
filtered_commands = [
|
|
230
|
+
command
|
|
231
|
+
for command in prior_commands
|
|
232
|
+
if not isinstance(command, StartFlowCommand)
|
|
233
|
+
or command.flow not in different_flow_names
|
|
234
|
+
]
|
|
235
|
+
|
|
236
|
+
return filtered_commands + commands
|
|
237
|
+
|
|
238
|
+
def _filter_slot_commands(
|
|
239
|
+
self,
|
|
240
|
+
prior_commands: List[Command],
|
|
241
|
+
commands: List[Command],
|
|
242
|
+
overlapping_slot_names: Set[str],
|
|
243
|
+
) -> Tuple[List[Command], List[Command]]:
|
|
244
|
+
"""Prioritize NLU commands over prior_commands in the case of same slot."""
|
|
245
|
+
filtered_prior_commands = (
|
|
246
|
+
rasa.dialogue_understanding.generator.utils.filter_slot_commands(
|
|
247
|
+
prior_commands, overlapping_slot_names
|
|
248
|
+
)
|
|
249
|
+
)
|
|
250
|
+
return filtered_prior_commands, commands
|
|
251
|
+
|
|
213
252
|
|
|
214
253
|
def _issue_set_slot_commands(
|
|
215
254
|
message: Message,
|
|
@@ -236,7 +236,7 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
|
|
|
236
236
|
domain = kwargs.get("domain")
|
|
237
237
|
commands = self._check_commands_against_slot_mappings(commands, tracker, domain)
|
|
238
238
|
|
|
239
|
-
return prior_commands
|
|
239
|
+
return self._check_commands_overlap(prior_commands, commands)
|
|
240
240
|
|
|
241
241
|
async def _predict_commands(
|
|
242
242
|
self,
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
from typing import Dict, Type
|
|
1
|
+
from typing import Dict, List, Set, Type
|
|
2
2
|
|
|
3
3
|
from rasa.dialogue_understanding.commands import (
|
|
4
4
|
CancelFlowCommand,
|
|
5
5
|
CannotHandleCommand,
|
|
6
6
|
ChitChatAnswerCommand,
|
|
7
7
|
Command,
|
|
8
|
+
CorrectSlotsCommand,
|
|
8
9
|
HumanHandoffCommand,
|
|
9
10
|
KnowledgeAnswerCommand,
|
|
10
11
|
RestartCommand,
|
|
11
12
|
SessionStartCommand,
|
|
13
|
+
SetSlotCommand,
|
|
12
14
|
SkipQuestionCommand,
|
|
13
15
|
)
|
|
14
16
|
from rasa.dialogue_understanding.commands.user_silence_command import UserSilenceCommand
|
|
@@ -43,3 +45,32 @@ triggerable_pattern_to_command_class: Dict[str, Type[Command]] = {
|
|
|
43
45
|
CannotHandlePatternFlowStackFrame.flow_id: CannotHandleCommand,
|
|
44
46
|
RestartPatternFlowStackFrame.flow_id: RestartCommand,
|
|
45
47
|
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def filter_slot_commands(
|
|
51
|
+
commands: List[Command], overlapping_slot_names: Set[str]
|
|
52
|
+
) -> List[Command]:
|
|
53
|
+
"""Filter out slot commands that set overlapping slots."""
|
|
54
|
+
filtered_commands = []
|
|
55
|
+
|
|
56
|
+
for command in commands:
|
|
57
|
+
if (
|
|
58
|
+
isinstance(command, SetSlotCommand)
|
|
59
|
+
and command.name in overlapping_slot_names
|
|
60
|
+
):
|
|
61
|
+
continue
|
|
62
|
+
|
|
63
|
+
if isinstance(command, CorrectSlotsCommand):
|
|
64
|
+
allowed_slots = [
|
|
65
|
+
slot
|
|
66
|
+
for slot in command.corrected_slots
|
|
67
|
+
if slot.name not in overlapping_slot_names
|
|
68
|
+
]
|
|
69
|
+
if not allowed_slots:
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
command.corrected_slots = allowed_slots
|
|
73
|
+
|
|
74
|
+
filtered_commands.append(command)
|
|
75
|
+
|
|
76
|
+
return filtered_commands
|
|
@@ -45,13 +45,12 @@ from rasa.shared.constants import (
|
|
|
45
45
|
from rasa.shared.core.constants import (
|
|
46
46
|
ACTION_TRIGGER_CHITCHAT,
|
|
47
47
|
FLOW_HASHES_SLOT,
|
|
48
|
-
KEY_ALLOW_NLU_CORRECTION,
|
|
49
|
-
KEY_MAPPING_TYPE,
|
|
50
48
|
SlotMappingType,
|
|
51
49
|
)
|
|
52
50
|
from rasa.shared.core.events import Event, SlotSet
|
|
53
51
|
from rasa.shared.core.flows import FlowsList
|
|
54
52
|
from rasa.shared.core.flows.steps.collect import CollectInformationFlowStep
|
|
53
|
+
from rasa.shared.core.slot_mappings import SlotMapping
|
|
55
54
|
from rasa.shared.core.slots import Slot
|
|
56
55
|
from rasa.shared.core.trackers import DialogueStateTracker
|
|
57
56
|
from rasa.shared.core.training_data.structures import StoryGraph
|
|
@@ -582,9 +581,9 @@ def clean_up_slot_command(
|
|
|
582
581
|
):
|
|
583
582
|
allow_nlu_correction = any(
|
|
584
583
|
[
|
|
585
|
-
mapping.
|
|
584
|
+
mapping.allow_nlu_correction is True
|
|
586
585
|
for mapping in slot.mappings
|
|
587
|
-
if mapping.
|
|
586
|
+
if mapping.type == SlotMappingType.FROM_LLM
|
|
588
587
|
]
|
|
589
588
|
)
|
|
590
589
|
|
|
@@ -742,12 +741,9 @@ def should_slot_be_set(
|
|
|
742
741
|
slot_mappings = slot.mappings
|
|
743
742
|
|
|
744
743
|
if not slot.mappings:
|
|
745
|
-
slot_mappings = [
|
|
744
|
+
slot_mappings = [SlotMapping(type=SlotMappingType.FROM_LLM)]
|
|
746
745
|
|
|
747
|
-
mapping_types = [
|
|
748
|
-
SlotMappingType(mapping.get(KEY_MAPPING_TYPE, SlotMappingType.FROM_LLM.value))
|
|
749
|
-
for mapping in slot_mappings
|
|
750
|
-
]
|
|
746
|
+
mapping_types = [mapping.type for mapping in slot_mappings]
|
|
751
747
|
|
|
752
748
|
slot_has_nlu_mapping = any(
|
|
753
749
|
[mapping_type.is_predefined_type() for mapping_type in mapping_types]
|
|
@@ -755,8 +751,8 @@ def should_slot_be_set(
|
|
|
755
751
|
slot_has_llm_mapping = any(
|
|
756
752
|
[mapping_type == SlotMappingType.FROM_LLM for mapping_type in mapping_types]
|
|
757
753
|
)
|
|
758
|
-
|
|
759
|
-
[mapping_type == SlotMappingType.
|
|
754
|
+
slot_has_controlled_mapping = any(
|
|
755
|
+
[mapping_type == SlotMappingType.CONTROLLED for mapping_type in mapping_types]
|
|
760
756
|
)
|
|
761
757
|
|
|
762
758
|
if set_slot_commands_so_far and command.extractor == SetSlotExtractor.LLM.value:
|
|
@@ -785,7 +781,9 @@ def should_slot_be_set(
|
|
|
785
781
|
):
|
|
786
782
|
return False
|
|
787
783
|
|
|
788
|
-
if
|
|
784
|
+
if slot_has_controlled_mapping and not (
|
|
785
|
+
slot_has_nlu_mapping or slot_has_llm_mapping
|
|
786
|
+
):
|
|
789
787
|
return False
|
|
790
788
|
|
|
791
789
|
return True
|
|
@@ -377,3 +377,53 @@ Test cases in **to_review** may require manual intervention because the E2E test
|
|
|
377
377
|
Review these cases to ensure that the converted test cases are correct and the list of commands and
|
|
378
378
|
bot responses is complete.
|
|
379
379
|
|
|
380
|
+
|
|
381
|
+
## Converting DUT test from one DSL to a another DSL
|
|
382
|
+
|
|
383
|
+
If you need to transform your commands from one DSL format to another
|
|
384
|
+
(for instance, updating `StartFlow(flow_name)` to `start flow_name` or `SetSlot(slot_name, slot_value)` to `set slot_name slot_value`),
|
|
385
|
+
you can use a standalone Python script:
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
python convert_dut_dsl.py --dut-tests-dir <path> --output-dir <path> --dsl-mappings <path>
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
The script has the following required parameters:
|
|
392
|
+
|
|
393
|
+
- `--dut-tests-dir <path>`: The directory (relative or absolute) containing your
|
|
394
|
+
existing Dialogue Understanding Tests (DUT). The script will look for `.yaml` or
|
|
395
|
+
`.yml` files within this folder (and subfolders).
|
|
396
|
+
- `--output-dir <path>`: The directory where transformed files will be saved. The folder
|
|
397
|
+
structure from your `dut-tests-dir` is preserved.
|
|
398
|
+
- `--dsl-mappings <path>`: The YAML file defining your DSL mapping rules.
|
|
399
|
+
|
|
400
|
+
The YAML file containing the mappings must adhere to the following format:
|
|
401
|
+
```yaml
|
|
402
|
+
mappings:
|
|
403
|
+
|
|
404
|
+
- from_dsl_regex: "^StartFlow\\(([^)]*)\\)$"
|
|
405
|
+
to_dsl_pattern: "start {1}"
|
|
406
|
+
|
|
407
|
+
- from_dsl_regex: "^SetSlot\\(([^,]+),\\s*(.*)\\)$"
|
|
408
|
+
to_dsl_pattern: "set {1} {2}"
|
|
409
|
+
|
|
410
|
+
- from_dsl_regex: "Clarify\(([\"\'a-zA-Z0-9_, ]*)\)"
|
|
411
|
+
to_dsl_pattern: "clarify {1}"
|
|
412
|
+
input_separators:
|
|
413
|
+
- ","
|
|
414
|
+
- " "
|
|
415
|
+
output_separator: " "
|
|
416
|
+
|
|
417
|
+
# ... add more mappings here
|
|
418
|
+
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
- `from_dsl_regex`: A regular expression (string) used to match the old DSL command.
|
|
422
|
+
Must include any necessary anchors (like ^ and $) and capturing groups ( ... ) for
|
|
423
|
+
dynamic parts.
|
|
424
|
+
- `to_dsl_pattern`: A string that contains placeholders like `{1}`, `{2}`, etc. Each
|
|
425
|
+
placeholder corresponds to a capturing group in from_dsl_regex, in order of
|
|
426
|
+
appearance.
|
|
427
|
+
- `input_separators`: Optional list of separators of the captured groups that can be replaced
|
|
428
|
+
with the `output_separator`
|
|
429
|
+
- `output_separator`: Output separator to replace separators from the list of `input_separators` in the captured group.
|
|
@@ -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
|
|
23
|
+
from rasa.shared.core.constants import 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
|
|
@@ -328,8 +328,8 @@ class TestCaseTrackerSimulator:
|
|
|
328
328
|
command.extractor = SetSlotExtractor.COMMAND_PAYLOAD_READER.value
|
|
329
329
|
# Use the SetSlotExtractor.NLU extractor if the slot mapping type is
|
|
330
330
|
# not FROM_LLM.
|
|
331
|
-
elif SlotMappingType.FROM_LLM
|
|
332
|
-
mapping
|
|
331
|
+
elif SlotMappingType.FROM_LLM not in [
|
|
332
|
+
mapping.type for mapping in slot_definition.mappings
|
|
333
333
|
]:
|
|
334
334
|
command.extractor = SetSlotExtractor.NLU.value
|
|
335
335
|
|
rasa/model_service.py
CHANGED
|
@@ -61,6 +61,7 @@ def main() -> None:
|
|
|
61
61
|
The API server can receive requests to train models, run bots, and manage
|
|
62
62
|
the lifecycle of models and bots.
|
|
63
63
|
"""
|
|
64
|
+
import rasa.telemetry
|
|
64
65
|
import rasa.utils.licensing
|
|
65
66
|
|
|
66
67
|
log_level = logging.DEBUG
|
|
@@ -74,6 +75,9 @@ def main() -> None:
|
|
|
74
75
|
|
|
75
76
|
rasa.utils.licensing.validate_license_from_env()
|
|
76
77
|
|
|
78
|
+
rasa.telemetry.initialize_telemetry()
|
|
79
|
+
rasa.telemetry.initialize_error_reporting()
|
|
80
|
+
|
|
77
81
|
try:
|
|
78
82
|
model_api.prepare_working_directories()
|
|
79
83
|
except Exception as e:
|
rasa/model_training.py
CHANGED
|
@@ -352,34 +352,31 @@ async def _train_graph(
|
|
|
352
352
|
model_name = determine_model_name(fixed_model_name, training_type)
|
|
353
353
|
full_model_path = Path(output_path, model_name)
|
|
354
354
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
355
|
+
await trainer.train(
|
|
356
|
+
model_configuration,
|
|
357
|
+
file_importer,
|
|
358
|
+
full_model_path,
|
|
359
|
+
force_retraining=force_full_training,
|
|
360
|
+
is_finetuning=is_finetuning,
|
|
361
|
+
)
|
|
362
|
+
if remote_storage:
|
|
363
|
+
push_model_to_remote_storage(full_model_path, remote_storage)
|
|
364
|
+
if not keep_local_model_copy:
|
|
365
|
+
full_model_path.unlink()
|
|
366
|
+
structlogger.info(
|
|
367
|
+
"model_training.train.finished_training",
|
|
368
|
+
event_info=(
|
|
369
|
+
f"Your Rasa model {model_name} is trained "
|
|
370
|
+
f"and saved at remote storage provider '{remote_storage}'."
|
|
371
|
+
),
|
|
372
|
+
)
|
|
373
|
+
else:
|
|
374
|
+
structlogger.info(
|
|
375
|
+
"model_training.train.finished_training",
|
|
376
|
+
event_info=(
|
|
377
|
+
f"Your Rasa model is trained and saved at '{full_model_path}'."
|
|
378
|
+
),
|
|
364
379
|
)
|
|
365
|
-
if remote_storage:
|
|
366
|
-
push_model_to_remote_storage(full_model_path, remote_storage)
|
|
367
|
-
if not keep_local_model_copy:
|
|
368
|
-
full_model_path.unlink()
|
|
369
|
-
structlogger.info(
|
|
370
|
-
"model_training.train.finished_training",
|
|
371
|
-
event_info=(
|
|
372
|
-
f"Your Rasa model {model_name} is trained "
|
|
373
|
-
f"and saved at remote storage provider '{remote_storage}'."
|
|
374
|
-
),
|
|
375
|
-
)
|
|
376
|
-
else:
|
|
377
|
-
structlogger.info(
|
|
378
|
-
"model_training.train.finished_training",
|
|
379
|
-
event_info=(
|
|
380
|
-
f"Your Rasa model is trained and saved at '{full_model_path}'."
|
|
381
|
-
),
|
|
382
|
-
)
|
|
383
380
|
|
|
384
381
|
return TrainingResult(str(full_model_path), 0)
|
|
385
382
|
|
rasa/shared/core/constants.py
CHANGED
|
@@ -143,6 +143,8 @@ SLOT_MAPPINGS = "mappings"
|
|
|
143
143
|
MAPPING_CONDITIONS = "conditions"
|
|
144
144
|
KEY_MAPPING_TYPE = "type"
|
|
145
145
|
KEY_ALLOW_NLU_CORRECTION = "allow_nlu_correction"
|
|
146
|
+
KEY_ACTION = "action"
|
|
147
|
+
KEY_RUN_ACTION_EVERY_TURN = "run_action_every_turn"
|
|
146
148
|
|
|
147
149
|
|
|
148
150
|
class SlotMappingType(Enum):
|
|
@@ -153,7 +155,7 @@ class SlotMappingType(Enum):
|
|
|
153
155
|
FROM_TRIGGER_INTENT = "from_trigger_intent"
|
|
154
156
|
FROM_TEXT = "from_text"
|
|
155
157
|
FROM_LLM = "from_llm"
|
|
156
|
-
|
|
158
|
+
CONTROLLED = "controlled"
|
|
157
159
|
|
|
158
160
|
def __str__(self) -> str:
|
|
159
161
|
"""Returns the string representation that should be used in config files."""
|
|
@@ -161,7 +163,9 @@ class SlotMappingType(Enum):
|
|
|
161
163
|
|
|
162
164
|
def is_predefined_type(self) -> bool:
|
|
163
165
|
"""Returns True if the mapping type is NLU-predefined."""
|
|
164
|
-
return not (
|
|
166
|
+
return not (
|
|
167
|
+
self == SlotMappingType.CONTROLLED or self == SlotMappingType.FROM_LLM
|
|
168
|
+
)
|
|
165
169
|
|
|
166
170
|
|
|
167
171
|
class SetSlotExtractor(Enum):
|
rasa/shared/core/domain.py
CHANGED
|
@@ -46,10 +46,8 @@ from rasa.shared.constants import (
|
|
|
46
46
|
)
|
|
47
47
|
from rasa.shared.core.constants import (
|
|
48
48
|
ACTION_SHOULD_SEND_DOMAIN,
|
|
49
|
-
ACTIVE_LOOP,
|
|
50
49
|
KEY_MAPPING_TYPE,
|
|
51
50
|
KNOWLEDGE_BASE_SLOT_NAMES,
|
|
52
|
-
MAPPING_CONDITIONS,
|
|
53
51
|
SLOT_MAPPINGS,
|
|
54
52
|
SlotMappingType,
|
|
55
53
|
)
|
|
@@ -290,8 +288,6 @@ class Domain:
|
|
|
290
288
|
responses = data.get(KEY_RESPONSES, {})
|
|
291
289
|
|
|
292
290
|
domain_slots = data.get(KEY_SLOTS, {})
|
|
293
|
-
if domain_slots:
|
|
294
|
-
rasa.shared.core.slot_mappings.validate_slot_mappings(domain_slots)
|
|
295
291
|
slots = cls.collect_slots(domain_slots)
|
|
296
292
|
domain_actions = data.get(KEY_ACTIONS, [])
|
|
297
293
|
actions = cls._collect_action_names(domain_actions)
|
|
@@ -1569,23 +1565,18 @@ class Domain:
|
|
|
1569
1565
|
matching_entities = []
|
|
1570
1566
|
|
|
1571
1567
|
for mapping in slot.mappings:
|
|
1572
|
-
mapping_conditions = mapping.
|
|
1573
|
-
if mapping
|
|
1574
|
-
SlotMappingType.FROM_ENTITY
|
|
1575
|
-
) or (
|
|
1568
|
+
mapping_conditions = mapping.conditions
|
|
1569
|
+
if mapping.type != SlotMappingType.FROM_ENTITY or (
|
|
1576
1570
|
mapping_conditions
|
|
1577
|
-
and mapping_conditions[0].
|
|
1571
|
+
and mapping_conditions[0].active_loop is not None
|
|
1578
1572
|
):
|
|
1579
1573
|
continue
|
|
1580
1574
|
|
|
1581
1575
|
for entity in entities:
|
|
1582
1576
|
if (
|
|
1583
|
-
entity.get(ENTITY_ATTRIBUTE_TYPE)
|
|
1584
|
-
|
|
1585
|
-
and entity.get(
|
|
1586
|
-
== mapping.get(ENTITY_ATTRIBUTE_ROLE)
|
|
1587
|
-
and entity.get(ENTITY_ATTRIBUTE_GROUP)
|
|
1588
|
-
== mapping.get(ENTITY_ATTRIBUTE_GROUP)
|
|
1577
|
+
entity.get(ENTITY_ATTRIBUTE_TYPE) == mapping.entity
|
|
1578
|
+
and entity.get(ENTITY_ATTRIBUTE_ROLE) == mapping.role
|
|
1579
|
+
and entity.get(ENTITY_ATTRIBUTE_GROUP) == mapping.group
|
|
1589
1580
|
):
|
|
1590
1581
|
matching_entities.append(entity.get("value"))
|
|
1591
1582
|
|
|
@@ -2017,19 +2008,19 @@ class Domain:
|
|
|
2017
2008
|
is the total number of mappings which have conditions attached.
|
|
2018
2009
|
"""
|
|
2019
2010
|
total_mappings = 0
|
|
2020
|
-
|
|
2011
|
+
controlled_mappings = 0
|
|
2021
2012
|
conditional_mappings = 0
|
|
2022
2013
|
|
|
2023
2014
|
for slot in self.slots:
|
|
2024
2015
|
total_mappings += len(slot.mappings)
|
|
2025
2016
|
for mapping in slot.mappings:
|
|
2026
|
-
if mapping
|
|
2027
|
-
|
|
2017
|
+
if mapping.type == SlotMappingType.CONTROLLED:
|
|
2018
|
+
controlled_mappings += 1
|
|
2028
2019
|
|
|
2029
|
-
if
|
|
2020
|
+
if mapping.conditions:
|
|
2030
2021
|
conditional_mappings += 1
|
|
2031
2022
|
|
|
2032
|
-
return
|
|
2023
|
+
return total_mappings, controlled_mappings, conditional_mappings
|
|
2033
2024
|
|
|
2034
2025
|
def does_custom_action_explicitly_need_domain(self, action_name: Text) -> bool:
|
|
2035
2026
|
"""Assert if action has explicitly stated that it needs domain.
|