rasa-pro 3.12.0.dev11__py3-none-any.whl → 3.12.0.dev12__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 +1 -20
- rasa/cli/shell.py +3 -3
- rasa/core/actions/action.py +7 -20
- rasa/core/actions/forms.py +5 -10
- rasa/core/channels/__init__.py +0 -2
- rasa/core/channels/voice_ready/audiocodes.py +23 -42
- rasa/core/channels/voice_stream/browser_audio.py +0 -1
- rasa/core/channels/voice_stream/call_state.py +1 -7
- rasa/core/channels/voice_stream/tts/azure.py +1 -2
- rasa/core/channels/voice_stream/tts/cartesia.py +3 -16
- rasa/core/channels/voice_stream/twilio_media_streams.py +1 -2
- rasa/core/channels/voice_stream/voice_channel.py +1 -2
- rasa/core/migrate.py +2 -2
- rasa/core/policies/flows/flow_executor.py +42 -36
- rasa/core/run.py +3 -4
- rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -2
- rasa/dialogue_understanding/commands/cancel_flow_command.py +4 -62
- 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 +2 -11
- 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 +15 -7
- rasa/dialogue_understanding/commands/skip_question_command.py +2 -2
- rasa/dialogue_understanding/commands/start_flow_command.py +2 -43
- rasa/dialogue_understanding/commands/utils.py +1 -1
- rasa/dialogue_understanding/constants.py +0 -1
- rasa/dialogue_understanding/generator/command_generator.py +73 -110
- rasa/dialogue_understanding/generator/command_parser.py +1 -1
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +3 -161
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +2 -10
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +3 -44
- rasa/dialogue_understanding/generator/single_step/command_prompt_template.jinja2 +79 -53
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +19 -11
- rasa/dialogue_understanding/generator/utils.py +1 -32
- rasa/dialogue_understanding/patterns/correction.py +1 -13
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +2 -62
- rasa/dialogue_understanding/processor/command_processor.py +28 -115
- rasa/dialogue_understanding/utils.py +0 -31
- rasa/dialogue_understanding_test/README.md +0 -50
- rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +3 -3
- rasa/model_service.py +0 -4
- rasa/model_training.py +27 -24
- rasa/shared/core/constants.py +3 -28
- rasa/shared/core/domain.py +20 -13
- rasa/shared/core/events.py +2 -13
- rasa/shared/core/flows/flow.py +0 -17
- rasa/shared/core/flows/flows_yaml_schema.json +0 -38
- rasa/shared/core/flows/steps/collect.py +1 -18
- rasa/shared/core/flows/utils.py +1 -16
- rasa/shared/core/slot_mappings.py +108 -144
- rasa/shared/core/slots.py +2 -23
- rasa/shared/core/trackers.py +1 -3
- rasa/shared/nlu/constants.py +0 -1
- rasa/shared/utils/llm.py +1 -1
- rasa/shared/utils/schemas/domain.yml +1 -0
- rasa/telemetry.py +13 -43
- rasa/utils/common.py +1 -0
- rasa/validator.py +82 -189
- rasa/version.py +1 -1
- {rasa_pro-3.12.0.dev11.dist-info → rasa_pro-3.12.0.dev12.dist-info}/METADATA +1 -1
- {rasa_pro-3.12.0.dev11.dist-info → rasa_pro-3.12.0.dev12.dist-info}/RECORD +67 -71
- rasa/core/actions/action_handle_digressions.py +0 -142
- rasa/core/channels/voice_stream/genesys.py +0 -331
- rasa/dialogue_understanding/commands/handle_digressions_command.py +0 -150
- rasa/dialogue_understanding/patterns/handle_digressions.py +0 -81
- {rasa_pro-3.12.0.dev11.dist-info → rasa_pro-3.12.0.dev12.dist-info}/NOTICE +0 -0
- {rasa_pro-3.12.0.dev11.dist-info → rasa_pro-3.12.0.dev12.dist-info}/WHEEL +0 -0
- {rasa_pro-3.12.0.dev11.dist-info → rasa_pro-3.12.0.dev12.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import copy
|
|
4
3
|
import re
|
|
5
4
|
from dataclasses import dataclass
|
|
6
5
|
from typing import Any, Dict, List
|
|
@@ -9,11 +8,8 @@ import structlog
|
|
|
9
8
|
|
|
10
9
|
from rasa.dialogue_understanding.commands.command import Command
|
|
11
10
|
from rasa.dialogue_understanding.patterns.cancel import CancelPatternFlowStackFrame
|
|
12
|
-
from rasa.dialogue_understanding.patterns.clarify import ClarifyPatternFlowStackFrame
|
|
13
11
|
from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack
|
|
14
|
-
from rasa.dialogue_understanding.stack.frames import
|
|
15
|
-
UserFlowStackFrame,
|
|
16
|
-
)
|
|
12
|
+
from rasa.dialogue_understanding.stack.frames import UserFlowStackFrame
|
|
17
13
|
from rasa.dialogue_understanding.stack.frames.flow_stack_frame import FlowStackFrameType
|
|
18
14
|
from rasa.dialogue_understanding.stack.utils import top_user_flow_frame
|
|
19
15
|
from rasa.shared.core.events import Event, FlowCancelled
|
|
@@ -93,8 +89,7 @@ class CancelFlowCommand(Command):
|
|
|
93
89
|
original_stack = original_tracker.stack
|
|
94
90
|
|
|
95
91
|
applied_events: List[Event] = []
|
|
96
|
-
|
|
97
|
-
initial_top_frame = stack.top()
|
|
92
|
+
|
|
98
93
|
user_frame = top_user_flow_frame(original_stack)
|
|
99
94
|
current_flow = user_frame.flow(all_flows) if user_frame else None
|
|
100
95
|
|
|
@@ -119,21 +114,6 @@ class CancelFlowCommand(Command):
|
|
|
119
114
|
if user_frame:
|
|
120
115
|
applied_events.append(FlowCancelled(user_frame.flow_id, user_frame.step_id))
|
|
121
116
|
|
|
122
|
-
if initial_top_frame and isinstance(
|
|
123
|
-
initial_top_frame, ClarifyPatternFlowStackFrame
|
|
124
|
-
):
|
|
125
|
-
structlogger.debug(
|
|
126
|
-
"command_executor.cancel_flow.cancel_clarification_options",
|
|
127
|
-
clarification_options=initial_top_frame.clarification_options,
|
|
128
|
-
)
|
|
129
|
-
applied_events += cancel_all_pending_clarification_options(
|
|
130
|
-
initial_top_frame,
|
|
131
|
-
original_stack,
|
|
132
|
-
canceled_frames,
|
|
133
|
-
all_flows,
|
|
134
|
-
stack,
|
|
135
|
-
)
|
|
136
|
-
|
|
137
117
|
return applied_events + tracker.create_stack_updated_events(stack)
|
|
138
118
|
|
|
139
119
|
def __hash__(self) -> int:
|
|
@@ -144,7 +124,7 @@ class CancelFlowCommand(Command):
|
|
|
144
124
|
|
|
145
125
|
def to_dsl(self) -> str:
|
|
146
126
|
"""Converts the command to a DSL string."""
|
|
147
|
-
return "
|
|
127
|
+
return "cancel flow"
|
|
148
128
|
|
|
149
129
|
@classmethod
|
|
150
130
|
def from_dsl(cls, match: re.Match, **kwargs: Any) -> CancelFlowCommand:
|
|
@@ -153,42 +133,4 @@ class CancelFlowCommand(Command):
|
|
|
153
133
|
|
|
154
134
|
@staticmethod
|
|
155
135
|
def regex_pattern() -> str:
|
|
156
|
-
return r"
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
def cancel_all_pending_clarification_options(
|
|
160
|
-
initial_top_frame: ClarifyPatternFlowStackFrame,
|
|
161
|
-
original_stack: DialogueStack,
|
|
162
|
-
canceled_frames: List[str],
|
|
163
|
-
all_flows: FlowsList,
|
|
164
|
-
stack: DialogueStack,
|
|
165
|
-
) -> List[FlowCancelled]:
|
|
166
|
-
"""Cancel all pending clarification options.
|
|
167
|
-
|
|
168
|
-
This is a special case when the assistant asks the user to clarify
|
|
169
|
-
which pending digression flow to start after the completion of an active flow.
|
|
170
|
-
If the user chooses to cancel all options, this function takes care of
|
|
171
|
-
updating the stack by removing all pending flow stack frames
|
|
172
|
-
listed as clarification options.
|
|
173
|
-
"""
|
|
174
|
-
clarification_names = set(initial_top_frame.names)
|
|
175
|
-
to_be_canceled_frames = []
|
|
176
|
-
applied_events = []
|
|
177
|
-
for frame in reversed(original_stack.frames):
|
|
178
|
-
if frame.frame_id in canceled_frames:
|
|
179
|
-
continue
|
|
180
|
-
|
|
181
|
-
to_be_canceled_frames.append(frame.frame_id)
|
|
182
|
-
if isinstance(frame, UserFlowStackFrame):
|
|
183
|
-
readable_flow_name = frame.flow(all_flows).readable_name()
|
|
184
|
-
if readable_flow_name in clarification_names:
|
|
185
|
-
stack.push(
|
|
186
|
-
CancelPatternFlowStackFrame(
|
|
187
|
-
canceled_name=readable_flow_name,
|
|
188
|
-
canceled_frames=copy.deepcopy(to_be_canceled_frames),
|
|
189
|
-
)
|
|
190
|
-
)
|
|
191
|
-
applied_events.append(FlowCancelled(frame.flow_id, frame.step_id))
|
|
192
|
-
to_be_canceled_frames.clear()
|
|
193
|
-
|
|
194
|
-
return applied_events
|
|
136
|
+
return r"^cancel flow$"
|
|
@@ -48,7 +48,7 @@ class ChangeFlowCommand(Command):
|
|
|
48
48
|
|
|
49
49
|
def to_dsl(self) -> str:
|
|
50
50
|
"""Converts the command to a DSL string."""
|
|
51
|
-
return "
|
|
51
|
+
return "change"
|
|
52
52
|
|
|
53
53
|
@staticmethod
|
|
54
54
|
def from_dsl(match: re.Match, **kwargs: Any) -> ChangeFlowCommand:
|
|
@@ -57,4 +57,4 @@ class ChangeFlowCommand(Command):
|
|
|
57
57
|
|
|
58
58
|
@staticmethod
|
|
59
59
|
def regex_pattern() -> str:
|
|
60
|
-
return r"
|
|
60
|
+
return r"^change"
|
|
@@ -59,7 +59,7 @@ class ChitChatAnswerCommand(FreeFormAnswerCommand):
|
|
|
59
59
|
|
|
60
60
|
def to_dsl(self) -> str:
|
|
61
61
|
"""Converts the command to a DSL string."""
|
|
62
|
-
return "
|
|
62
|
+
return "offtopic reply"
|
|
63
63
|
|
|
64
64
|
@classmethod
|
|
65
65
|
def from_dsl(cls, match: re.Match, **kwargs: Any) -> ChitChatAnswerCommand:
|
|
@@ -68,4 +68,4 @@ class ChitChatAnswerCommand(FreeFormAnswerCommand):
|
|
|
68
68
|
|
|
69
69
|
@staticmethod
|
|
70
70
|
def regex_pattern() -> str:
|
|
71
|
-
return r"
|
|
71
|
+
return r"^offtopic reply$"
|
|
@@ -89,7 +89,7 @@ class ClarifyCommand(Command):
|
|
|
89
89
|
|
|
90
90
|
def to_dsl(self) -> str:
|
|
91
91
|
"""Converts the command to a DSL string."""
|
|
92
|
-
return f"
|
|
92
|
+
return f"disambiguate flows {' '.join(self.options)}"
|
|
93
93
|
|
|
94
94
|
@classmethod
|
|
95
95
|
def from_dsl(cls, match: re.Match, **kwargs: Any) -> Optional[ClarifyCommand]:
|
|
@@ -99,4 +99,4 @@ class ClarifyCommand(Command):
|
|
|
99
99
|
|
|
100
100
|
@staticmethod
|
|
101
101
|
def regex_pattern() -> str:
|
|
102
|
-
return r"
|
|
102
|
+
return r"^disambiguate flows([\"\'a-zA-Z0-9_, ]*)$"
|
|
@@ -31,7 +31,6 @@ class CorrectedSlot:
|
|
|
31
31
|
|
|
32
32
|
name: str
|
|
33
33
|
value: Any
|
|
34
|
-
filled_by: Optional[str] = None
|
|
35
34
|
|
|
36
35
|
|
|
37
36
|
@dataclass
|
|
@@ -55,9 +54,7 @@ class CorrectSlotsCommand(Command):
|
|
|
55
54
|
try:
|
|
56
55
|
return CorrectSlotsCommand(
|
|
57
56
|
corrected_slots=[
|
|
58
|
-
CorrectedSlot(
|
|
59
|
-
s["name"], value=s["value"], filled_by=s.get("filled_by", None)
|
|
60
|
-
)
|
|
57
|
+
CorrectedSlot(s["name"], value=s["value"])
|
|
61
58
|
for s in data["corrected_slots"]
|
|
62
59
|
]
|
|
63
60
|
)
|
|
@@ -138,10 +135,7 @@ class CorrectSlotsCommand(Command):
|
|
|
138
135
|
proposed_slots = {}
|
|
139
136
|
for corrected_slot in self.corrected_slots:
|
|
140
137
|
if tracker.get_slot(corrected_slot.name) != corrected_slot.value:
|
|
141
|
-
proposed_slots[corrected_slot.name] =
|
|
142
|
-
"value": corrected_slot.value,
|
|
143
|
-
"filled_by": corrected_slot.filled_by,
|
|
144
|
-
}
|
|
138
|
+
proposed_slots[corrected_slot.name] = corrected_slot.value
|
|
145
139
|
else:
|
|
146
140
|
structlogger.debug(
|
|
147
141
|
"command_executor.skip_correction.slot_already_set", command=self
|
|
@@ -246,9 +240,6 @@ class CorrectSlotsCommand(Command):
|
|
|
246
240
|
corrected_slots=proposed_slots,
|
|
247
241
|
reset_flow_id=earliest_collect.flow_id if earliest_collect else None,
|
|
248
242
|
reset_step_id=earliest_collect.step.id if earliest_collect else None,
|
|
249
|
-
new_slot_values=[
|
|
250
|
-
value.get("value") for slot, value in proposed_slots.items()
|
|
251
|
-
],
|
|
252
243
|
)
|
|
253
244
|
|
|
254
245
|
def run_command_on_tracker(
|
|
@@ -66,7 +66,7 @@ class HumanHandoffCommand(Command):
|
|
|
66
66
|
|
|
67
67
|
def to_dsl(self) -> str:
|
|
68
68
|
"""Converts the command to a DSL string."""
|
|
69
|
-
return "
|
|
69
|
+
return "hand over"
|
|
70
70
|
|
|
71
71
|
@classmethod
|
|
72
72
|
def from_dsl(cls, match: re.Match, **kwargs: Any) -> HumanHandoffCommand:
|
|
@@ -75,4 +75,4 @@ class HumanHandoffCommand(Command):
|
|
|
75
75
|
|
|
76
76
|
@staticmethod
|
|
77
77
|
def regex_pattern() -> str:
|
|
78
|
-
return r"
|
|
78
|
+
return r"^hand over$"
|
|
@@ -59,7 +59,7 @@ class KnowledgeAnswerCommand(FreeFormAnswerCommand):
|
|
|
59
59
|
|
|
60
60
|
def to_dsl(self) -> str:
|
|
61
61
|
"""Converts the command to a DSL string."""
|
|
62
|
-
return "
|
|
62
|
+
return "provide info"
|
|
63
63
|
|
|
64
64
|
@classmethod
|
|
65
65
|
def from_dsl(cls, match: re.Match, **kwargs: Any) -> KnowledgeAnswerCommand:
|
|
@@ -68,4 +68,4 @@ class KnowledgeAnswerCommand(FreeFormAnswerCommand):
|
|
|
68
68
|
|
|
69
69
|
@staticmethod
|
|
70
70
|
def regex_pattern() -> str:
|
|
71
|
-
return r"
|
|
71
|
+
return r"^provide info$"
|
|
@@ -60,7 +60,7 @@ class RepeatBotMessagesCommand(Command):
|
|
|
60
60
|
|
|
61
61
|
def to_dsl(self) -> str:
|
|
62
62
|
"""Converts the command to a DSL string."""
|
|
63
|
-
return "
|
|
63
|
+
return "repeat message"
|
|
64
64
|
|
|
65
65
|
@classmethod
|
|
66
66
|
def from_dsl(cls, match: re.Match, **kwargs: Any) -> RepeatBotMessagesCommand:
|
|
@@ -69,4 +69,4 @@ class RepeatBotMessagesCommand(Command):
|
|
|
69
69
|
|
|
70
70
|
@staticmethod
|
|
71
71
|
def regex_pattern() -> str:
|
|
72
|
-
return r"
|
|
72
|
+
return r"^repeat message$"
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
from dataclasses import dataclass
|
|
5
|
+
from enum import Enum
|
|
5
6
|
from typing import Any, Dict, List
|
|
6
7
|
|
|
7
8
|
import structlog
|
|
@@ -18,7 +19,6 @@ from rasa.dialogue_understanding.stack.utils import (
|
|
|
18
19
|
get_collect_steps_excluding_ask_before_filling_for_active_flow,
|
|
19
20
|
)
|
|
20
21
|
from rasa.shared.constants import ROUTE_TO_CALM_SLOT
|
|
21
|
-
from rasa.shared.core.constants import SetSlotExtractor
|
|
22
22
|
from rasa.shared.core.events import Event, SlotSet
|
|
23
23
|
from rasa.shared.core.flows import FlowsList
|
|
24
24
|
from rasa.shared.core.trackers import DialogueStateTracker
|
|
@@ -27,6 +27,17 @@ from rasa.shared.nlu.constants import SET_SLOT_COMMAND
|
|
|
27
27
|
structlogger = structlog.get_logger()
|
|
28
28
|
|
|
29
29
|
|
|
30
|
+
class SetSlotExtractor(Enum):
|
|
31
|
+
"""The extractors that can set a slot."""
|
|
32
|
+
|
|
33
|
+
LLM = "LLM"
|
|
34
|
+
COMMAND_PAYLOAD_READER = "CommandPayloadReader"
|
|
35
|
+
NLU = "NLU"
|
|
36
|
+
|
|
37
|
+
def __str__(self) -> str:
|
|
38
|
+
return self.value
|
|
39
|
+
|
|
40
|
+
|
|
30
41
|
def get_flows_predicted_to_start_from_tracker(
|
|
31
42
|
tracker: DialogueStateTracker,
|
|
32
43
|
) -> List[str]:
|
|
@@ -126,7 +137,6 @@ class SetSlotCommand(Command):
|
|
|
126
137
|
in {
|
|
127
138
|
SetSlotExtractor.LLM.value,
|
|
128
139
|
SetSlotExtractor.COMMAND_PAYLOAD_READER.value,
|
|
129
|
-
SetSlotExtractor.NLU.value,
|
|
130
140
|
}
|
|
131
141
|
):
|
|
132
142
|
# Get the other predicted flows from the most recent message on the tracker.
|
|
@@ -144,9 +154,7 @@ class SetSlotCommand(Command):
|
|
|
144
154
|
return []
|
|
145
155
|
|
|
146
156
|
structlogger.debug("command_executor.set_slot", command=self)
|
|
147
|
-
return [
|
|
148
|
-
SlotSet(self.name, slot.coerce_value(self.value), filled_by=self.extractor)
|
|
149
|
-
]
|
|
157
|
+
return [SlotSet(self.name, slot.coerce_value(self.value))]
|
|
150
158
|
|
|
151
159
|
def __hash__(self) -> int:
|
|
152
160
|
return hash(self.value) + hash(self.name)
|
|
@@ -162,7 +170,7 @@ class SetSlotCommand(Command):
|
|
|
162
170
|
|
|
163
171
|
def to_dsl(self) -> str:
|
|
164
172
|
"""Converts the command to a DSL string."""
|
|
165
|
-
return f"
|
|
173
|
+
return f"set slot {self.name} {self.value}"
|
|
166
174
|
|
|
167
175
|
@classmethod
|
|
168
176
|
def from_dsl(cls, match: re.Match, **kwargs: Any) -> SetSlotCommand:
|
|
@@ -173,4 +181,4 @@ class SetSlotCommand(Command):
|
|
|
173
181
|
|
|
174
182
|
@staticmethod
|
|
175
183
|
def regex_pattern() -> str:
|
|
176
|
-
return r"""
|
|
184
|
+
return r"""^set slot ['"]?([a-zA-Z_][a-zA-Z0-9_-]*)['"]? ['"]?(.+?)['"]?$"""
|
|
@@ -75,7 +75,7 @@ class SkipQuestionCommand(Command):
|
|
|
75
75
|
|
|
76
76
|
def to_dsl(self) -> str:
|
|
77
77
|
"""Converts the command to a DSL string."""
|
|
78
|
-
return "
|
|
78
|
+
return "skip"
|
|
79
79
|
|
|
80
80
|
@classmethod
|
|
81
81
|
def from_dsl(cls, match: re.Match, **kwargs: Any) -> SkipQuestionCommand:
|
|
@@ -84,4 +84,4 @@ class SkipQuestionCommand(Command):
|
|
|
84
84
|
|
|
85
85
|
@staticmethod
|
|
86
86
|
def regex_pattern() -> str:
|
|
87
|
-
return r"
|
|
87
|
+
return r"^skip$"
|
|
@@ -7,11 +7,6 @@ from typing import Any, Dict, List, Optional
|
|
|
7
7
|
import structlog
|
|
8
8
|
|
|
9
9
|
from rasa.dialogue_understanding.commands.command import Command
|
|
10
|
-
from rasa.dialogue_understanding.patterns.clarify import FLOW_PATTERN_CLARIFICATION
|
|
11
|
-
from rasa.dialogue_understanding.patterns.continue_interrupted import (
|
|
12
|
-
ContinueInterruptedPatternFlowStackFrame,
|
|
13
|
-
)
|
|
14
|
-
from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack
|
|
15
10
|
from rasa.dialogue_understanding.stack.frames.flow_stack_frame import (
|
|
16
11
|
FlowStackFrameType,
|
|
17
12
|
UserFlowStackFrame,
|
|
@@ -73,10 +68,6 @@ class StartFlowCommand(Command):
|
|
|
73
68
|
applied_events: List[Event] = []
|
|
74
69
|
|
|
75
70
|
if self.flow in user_flows_on_the_stack(stack):
|
|
76
|
-
top_frame = stack.top()
|
|
77
|
-
if top_frame is not None and top_frame.type() == FLOW_PATTERN_CLARIFICATION:
|
|
78
|
-
return self.change_flow_frame_position_in_the_stack(stack, tracker)
|
|
79
|
-
|
|
80
71
|
structlogger.debug(
|
|
81
72
|
"command_executor.skip_command.already_started_flow", command=self
|
|
82
73
|
)
|
|
@@ -119,7 +110,7 @@ class StartFlowCommand(Command):
|
|
|
119
110
|
|
|
120
111
|
def to_dsl(self) -> str:
|
|
121
112
|
"""Converts the command to a DSL string."""
|
|
122
|
-
return f"
|
|
113
|
+
return f"start flow {self.flow}"
|
|
123
114
|
|
|
124
115
|
@classmethod
|
|
125
116
|
def from_dsl(cls, match: re.Match, **kwargs: Any) -> Optional[StartFlowCommand]:
|
|
@@ -128,36 +119,4 @@ class StartFlowCommand(Command):
|
|
|
128
119
|
|
|
129
120
|
@staticmethod
|
|
130
121
|
def regex_pattern() -> str:
|
|
131
|
-
return r"
|
|
132
|
-
|
|
133
|
-
def change_flow_frame_position_in_the_stack(
|
|
134
|
-
self, stack: DialogueStack, tracker: DialogueStateTracker
|
|
135
|
-
) -> List[Event]:
|
|
136
|
-
"""Changes the position of the flow frame in the stack.
|
|
137
|
-
|
|
138
|
-
This is a special case when pattern clarification is the active flow and
|
|
139
|
-
the same flow is selected to start. In this case, the existing flow frame
|
|
140
|
-
should be moved up in the stack.
|
|
141
|
-
"""
|
|
142
|
-
frames = stack.frames[:]
|
|
143
|
-
|
|
144
|
-
for idx, frame in enumerate(frames):
|
|
145
|
-
if isinstance(frame, UserFlowStackFrame) and frame.flow_id == self.flow:
|
|
146
|
-
structlogger.debug(
|
|
147
|
-
"command_executor.change_flow_position_during_clarification",
|
|
148
|
-
command=self,
|
|
149
|
-
index=idx,
|
|
150
|
-
)
|
|
151
|
-
# pop the continue interrupted flow frame if it exists
|
|
152
|
-
next_frame = frames[idx + 1] if idx + 1 < len(frames) else None
|
|
153
|
-
if (
|
|
154
|
-
isinstance(next_frame, ContinueInterruptedPatternFlowStackFrame)
|
|
155
|
-
and next_frame.previous_flow_name == self.flow
|
|
156
|
-
):
|
|
157
|
-
stack.frames.pop(idx + 1)
|
|
158
|
-
# move up the existing flow from the stack
|
|
159
|
-
stack.frames.pop(idx)
|
|
160
|
-
stack.push(frame)
|
|
161
|
-
return tracker.create_stack_updated_events(stack)
|
|
162
|
-
|
|
163
|
-
return []
|
|
122
|
+
return r"^start flow ['\"]?([a-zA-Z0-9_-]+)['\"]?$"
|
|
@@ -27,7 +27,7 @@ def extract_cleaned_options(options_str: str) -> List[str]:
|
|
|
27
27
|
"""Extract and clean options from a string."""
|
|
28
28
|
return sorted(
|
|
29
29
|
opt.strip().strip('"').strip("'")
|
|
30
|
-
for opt in options_str.split("
|
|
30
|
+
for opt in options_str.split(" ")
|
|
31
31
|
if opt.strip()
|
|
32
32
|
)
|
|
33
33
|
|
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from typing import Any, Dict, List, Optional,
|
|
2
|
+
from typing import Any, Dict, List, Optional, Text
|
|
3
3
|
|
|
4
4
|
import structlog
|
|
5
5
|
|
|
6
6
|
from rasa.dialogue_understanding.commands import (
|
|
7
7
|
Command,
|
|
8
|
-
CorrectSlotsCommand,
|
|
9
8
|
ErrorCommand,
|
|
10
9
|
SetSlotCommand,
|
|
11
10
|
StartFlowCommand,
|
|
12
11
|
)
|
|
13
|
-
from rasa.dialogue_understanding.
|
|
14
|
-
_handle_via_nlu_in_coexistence,
|
|
15
|
-
)
|
|
12
|
+
from rasa.dialogue_understanding.commands.set_slot_command import SetSlotExtractor
|
|
16
13
|
from rasa.shared.constants import (
|
|
17
14
|
RASA_PATTERN_INTERNAL_ERROR_USER_INPUT_EMPTY,
|
|
18
15
|
RASA_PATTERN_INTERNAL_ERROR_USER_INPUT_TOO_LONG,
|
|
19
16
|
)
|
|
17
|
+
from rasa.shared.core.constants import SlotMappingType
|
|
20
18
|
from rasa.shared.core.domain import Domain
|
|
21
19
|
from rasa.shared.core.flows import FlowsList
|
|
20
|
+
from rasa.shared.core.slot_mappings import SlotFillingManager
|
|
22
21
|
from rasa.shared.core.trackers import DialogueStateTracker
|
|
23
22
|
from rasa.shared.nlu.constants import (
|
|
24
23
|
COMMANDS,
|
|
@@ -93,9 +92,9 @@ class CommandGenerator:
|
|
|
93
92
|
)
|
|
94
93
|
|
|
95
94
|
for message in messages:
|
|
96
|
-
if
|
|
97
|
-
#
|
|
98
|
-
#
|
|
95
|
+
if message.get(COMMANDS):
|
|
96
|
+
# do not overwrite commands if they are already present
|
|
97
|
+
# i.e. another command generator already predicted commands
|
|
99
98
|
continue
|
|
100
99
|
|
|
101
100
|
commands = await self._evaluate_and_predict(
|
|
@@ -107,6 +106,9 @@ class CommandGenerator:
|
|
|
107
106
|
commands = self._check_commands_against_startable_flows(
|
|
108
107
|
commands, startable_flows
|
|
109
108
|
)
|
|
109
|
+
commands = self._check_commands_against_slot_mappings(
|
|
110
|
+
commands, tracker, domain
|
|
111
|
+
)
|
|
110
112
|
commands_dicts = [command.as_dict() for command in commands]
|
|
111
113
|
message.set(COMMANDS, commands_dicts, add_to_output=True)
|
|
112
114
|
|
|
@@ -200,94 +202,6 @@ class CommandGenerator:
|
|
|
200
202
|
"""
|
|
201
203
|
raise NotImplementedError()
|
|
202
204
|
|
|
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
|
-
|
|
291
205
|
def _check_commands_against_startable_flows(
|
|
292
206
|
self, commands: List[Command], startable_flows: FlowsList
|
|
293
207
|
) -> List[Command]:
|
|
@@ -364,21 +278,70 @@ class CommandGenerator:
|
|
|
364
278
|
return len(message.get(TEXT, "").strip()) == 0
|
|
365
279
|
|
|
366
280
|
@staticmethod
|
|
367
|
-
def
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
281
|
+
def _check_commands_against_slot_mappings(
|
|
282
|
+
commands: List[Command],
|
|
283
|
+
tracker: DialogueStateTracker,
|
|
284
|
+
domain: Optional[Domain] = None,
|
|
285
|
+
) -> List[Command]:
|
|
286
|
+
"""Check if the LLM-issued slot commands are fillable.
|
|
287
|
+
|
|
288
|
+
The LLM-issued slot commands are fillable if the slot
|
|
289
|
+
mappings are satisfied.
|
|
290
|
+
"""
|
|
291
|
+
if not domain:
|
|
292
|
+
return commands
|
|
293
|
+
|
|
294
|
+
llm_fillable_slot_names = [
|
|
295
|
+
command.name
|
|
296
|
+
for command in commands
|
|
297
|
+
if isinstance(command, SetSlotCommand)
|
|
298
|
+
and command.extractor == SetSlotExtractor.LLM.value
|
|
299
|
+
]
|
|
300
|
+
|
|
301
|
+
if not llm_fillable_slot_names:
|
|
302
|
+
return commands
|
|
303
|
+
|
|
304
|
+
llm_fillable_slots = [
|
|
305
|
+
slot for slot in domain.slots if slot.name in llm_fillable_slot_names
|
|
371
306
|
]
|
|
372
307
|
|
|
308
|
+
slot_filling_manager = SlotFillingManager(domain, tracker)
|
|
309
|
+
slots_to_be_removed = []
|
|
373
310
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
for
|
|
382
|
-
|
|
311
|
+
structlogger.debug(
|
|
312
|
+
"command_processor.check_commands_against_slot_mappings.active_flow",
|
|
313
|
+
active_flow=tracker.active_flow,
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
for slot in llm_fillable_slots:
|
|
317
|
+
should_fill_slot = False
|
|
318
|
+
for mapping in slot.mappings:
|
|
319
|
+
mapping_type = SlotMappingType(mapping.get("type"))
|
|
320
|
+
|
|
321
|
+
should_fill_slot = slot_filling_manager.should_fill_slot(
|
|
322
|
+
slot.name, mapping_type, mapping
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
if should_fill_slot:
|
|
326
|
+
break
|
|
327
|
+
|
|
328
|
+
if not should_fill_slot:
|
|
329
|
+
structlogger.debug(
|
|
330
|
+
"command_processor.check_commands_against_slot_mappings.slot_not_fillable",
|
|
331
|
+
slot_name=slot.name,
|
|
332
|
+
)
|
|
333
|
+
slots_to_be_removed.append(slot.name)
|
|
334
|
+
|
|
335
|
+
if not slots_to_be_removed:
|
|
336
|
+
return commands
|
|
337
|
+
|
|
338
|
+
filtered_commands = [
|
|
339
|
+
command
|
|
340
|
+
for command in commands
|
|
341
|
+
if not (
|
|
342
|
+
isinstance(command, SetSlotCommand)
|
|
343
|
+
and command.name in slots_to_be_removed
|
|
344
|
+
)
|
|
345
|
+
]
|
|
383
346
|
|
|
384
|
-
|
|
347
|
+
return filtered_commands
|
|
@@ -125,7 +125,7 @@ def _parse_standard_commands(
|
|
|
125
125
|
commands: List[Command] = []
|
|
126
126
|
for command_clz in standard_commands:
|
|
127
127
|
pattern = _get_compiled_pattern(command_clz.regex_pattern())
|
|
128
|
-
if match := pattern.search(action):
|
|
128
|
+
if match := pattern.search(action.strip()):
|
|
129
129
|
parsed_command = command_clz.from_dsl(match, **kwargs)
|
|
130
130
|
if _additional_parsing_fn := _get_additional_parsing_logic(command_clz):
|
|
131
131
|
parsed_command = _additional_parsing_fn(parsed_command, flows, **kwargs)
|