rasa-pro 3.14.0.dev6__py3-none-any.whl → 3.14.0.dev7__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/agents/exceptions.py +31 -1
- rasa/agents/protocol/a2a/a2a_agent.py +1 -1
- rasa/agents/protocol/mcp/mcp_base_agent.py +7 -6
- rasa/agents/protocol/mcp/mcp_task_agent.py +1 -1
- rasa/agents/utils.py +5 -0
- rasa/agents/validation.py +484 -0
- rasa/api.py +9 -6
- rasa/cli/arguments/train.py +2 -0
- rasa/cli/interactive.py +2 -0
- rasa/cli/train.py +2 -0
- rasa/cli/utils.py +85 -1
- rasa/core/actions/action.py +2 -6
- rasa/core/available_agents.py +47 -26
- rasa/core/channels/inspector/dist/assets/{arc-63212852.js → arc-cce7e0a8.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-eecf6b13.js → blockDiagram-38ab4fdb-e2a49be7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-8f798a9a.js → c4Diagram-3d4e48cf-3def7895.js} +1 -1
- rasa/core/channels/inspector/dist/assets/channel-858c2c20.js +1 -0
- rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-df71a04c.js → classDiagram-70f12bd4-e66fe4df.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-9b275968.js → classDiagram-v2-f2320105-eb874aaa.js} +1 -1
- rasa/core/channels/inspector/dist/assets/clone-4b80996c.js +1 -0
- rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-1c669cad.js → createText-2e5e7dd3-cf934643.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-b1553799.js → edges-e0da2a9e-8fdf9155.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-112388d6.js → erDiagram-9861fffd-6106fb96.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-fdebec47.js → flowDb-956e92f1-4c2bb040.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-6280ede1.js → flowDiagram-66a62f08-f0ff96af.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-16f09b7a.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-e1dc03e5.js → flowchart-elk-definition-4a651766-a21707ec.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-83f68c51.js → ganttDiagram-c361ad54-c165acb1.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-22f8666f.js → gitGraphDiagram-72cf32ee-b0564cf1.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{graph-ca9e6217.js → graph-e557e67a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-3862675e-c5ceb692.js → index-3862675e-1ce60e9e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-3e293924.js → index-996fe816.js} +173 -173
- rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-faa9999b.js → infoDiagram-f8f76790-893569e2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-c4dda8d9.js → journeyDiagram-49397b02-c29c864f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-d4307784.js → layout-649a5eae.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-0567aaa7.js → line-0e5685ed.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-c11b95cf.js → linear-eaa320bd.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-0c7d3ca9.js → mindmap-definition-fc14e90a-f35df9e6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-34b433fa.js → pieDiagram-8a3498a8-78339e96.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-4cab816e.js → quadrantDiagram-120e2f19-9b5f2f14.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-8c22fa9e.js → requirementDiagram-deff3bca-d05ddb3a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-70ce9e8e.js → sankeyDiagram-04a897e0-d9be5dfd.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-fbcd7fc9.js → sequenceDiagram-704730f1-0f1c4348.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-45f05ea6.js → stateDiagram-587899a1-9ddf63b3.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-beab1ea6.js → stateDiagram-v2-d93cdb3a-bc2b81ed.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-2f29dbd5.js → styles-6aaf32cf-0a287936.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9a916d00-951eac83.js → styles-9a916d00-e3941990.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-c10674c1-897fbfdd.js → styles-c10674c1-ce4eca24.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-d667fac1.js → svgDrawCommon-08f97a94-d822b1a8.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-e3205144.js → timeline-definition-85554ec2-e144c7a7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-4abeb0e2.js → xychartDiagram-e933f94c-ab7f4e14.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +1 -1
- rasa/core/channels/inspector/src/App.tsx +28 -4
- rasa/core/channels/inspector/src/components/DialogueAgentStack.tsx +108 -0
- rasa/core/channels/inspector/src/components/{DialogueStack.tsx → DialogueHistoryStack.tsx} +7 -8
- rasa/core/channels/inspector/src/helpers/formatters.test.ts +4 -0
- rasa/core/channels/inspector/src/helpers/utils.test.ts +127 -0
- rasa/core/channels/inspector/src/helpers/utils.ts +66 -1
- rasa/core/channels/inspector/src/types.ts +17 -0
- rasa/core/policies/flows/flow_executor.py +52 -31
- rasa/dialogue_understanding/commands/cancel_flow_command.py +2 -81
- rasa/dialogue_understanding/commands/start_flow_command.py +18 -113
- rasa/dialogue_understanding/commands/utils.py +118 -0
- rasa/dialogue_understanding/patterns/clarify.py +3 -14
- rasa/dialogue_understanding/patterns/continue_interrupted.py +185 -114
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +17 -23
- rasa/dialogue_understanding/stack/utils.py +43 -4
- rasa/dialogue_understanding/utils.py +24 -4
- rasa/model_training.py +8 -6
- rasa/shared/constants.py +3 -0
- rasa/shared/core/constants.py +5 -6
- rasa/shared/utils/health_check/health_check.py +7 -3
- rasa/shared/utils/mcp/server_connection.py +26 -6
- rasa/version.py +1 -1
- {rasa_pro-3.14.0.dev6.dist-info → rasa_pro-3.14.0.dev7.dist-info}/METADATA +1 -1
- {rasa_pro-3.14.0.dev6.dist-info → rasa_pro-3.14.0.dev7.dist-info}/RECORD +79 -76
- rasa/core/channels/inspector/dist/assets/channel-0cd70adf.js +0 -1
- rasa/core/channels/inspector/dist/assets/clone-a0f9c4ed.js +0 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-de9cc4aa.js +0 -1
- {rasa_pro-3.14.0.dev6.dist-info → rasa_pro-3.14.0.dev7.dist-info}/NOTICE +0 -0
- {rasa_pro-3.14.0.dev6.dist-info → rasa_pro-3.14.0.dev7.dist-info}/WHEEL +0 -0
- {rasa_pro-3.14.0.dev6.dist-info → rasa_pro-3.14.0.dev7.dist-info}/entry_points.txt +0 -0
|
@@ -1,31 +1,47 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from typing import Any, Dict, List, Optional, Text
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Any, Dict, List, Optional, Text, Tuple
|
|
5
|
+
|
|
6
|
+
import structlog
|
|
5
7
|
|
|
6
8
|
from rasa.core.actions.action import Action
|
|
7
9
|
from rasa.core.channels import OutputChannel
|
|
8
10
|
from rasa.core.nlg import NaturalLanguageGenerator
|
|
11
|
+
from rasa.dialogue_understanding.commands.utils import (
|
|
12
|
+
resume_flow,
|
|
13
|
+
)
|
|
14
|
+
from rasa.dialogue_understanding.patterns.cancel import CancelPatternFlowStackFrame
|
|
15
|
+
from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack
|
|
9
16
|
from rasa.dialogue_understanding.stack.frames import PatternFlowStackFrame
|
|
10
|
-
from rasa.dialogue_understanding.stack.
|
|
17
|
+
from rasa.dialogue_understanding.stack.frames.dialogue_stack_frame import (
|
|
18
|
+
DialogueStackFrame,
|
|
19
|
+
)
|
|
20
|
+
from rasa.dialogue_understanding.stack.frames.flow_stack_frame import (
|
|
21
|
+
AgentStackFrame,
|
|
22
|
+
FlowStackFrameType,
|
|
23
|
+
UserFlowStackFrame,
|
|
24
|
+
)
|
|
25
|
+
from rasa.dialogue_understanding.stack.utils import (
|
|
26
|
+
get_active_continue_interrupted_pattern_frame,
|
|
27
|
+
)
|
|
11
28
|
from rasa.shared.constants import RASA_DEFAULT_FLOW_PATTERN_PREFIX
|
|
12
29
|
from rasa.shared.core.constants import (
|
|
13
|
-
|
|
14
|
-
ACTION_CANCEL_INTERRUPTED_FLOW,
|
|
30
|
+
ACTION_CANCEL_INTERRUPTED_FLOWS,
|
|
15
31
|
ACTION_CONTINUE_INTERRUPTED_FLOW,
|
|
16
|
-
ACTION_SET_INTERRUPTED_FLOWS,
|
|
17
32
|
)
|
|
18
33
|
from rasa.shared.core.domain import Domain
|
|
19
|
-
from rasa.shared.core.events import Event, SlotSet
|
|
34
|
+
from rasa.shared.core.events import AgentCancelled, Event, FlowCancelled, SlotSet
|
|
20
35
|
from rasa.shared.core.trackers import DialogueStateTracker
|
|
21
36
|
|
|
22
37
|
FLOW_PATTERN_CONTINUE_INTERRUPTED = (
|
|
23
38
|
RASA_DEFAULT_FLOW_PATTERN_PREFIX + "continue_interrupted"
|
|
24
39
|
)
|
|
25
|
-
INTERRUPTED_FLOWS_SLOT = "interrupted_flows"
|
|
26
40
|
INTERRUPTED_FLOW_TO_CONTINUE_SLOT = "interrupted_flow_to_continue"
|
|
27
|
-
|
|
28
|
-
|
|
41
|
+
CONTINUE_INTERRUPTED_FLOW_CONFIRMATION_SLOT = "continue_interrupted_flow_confirmation"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
structlogger = structlog.get_logger()
|
|
29
45
|
|
|
30
46
|
|
|
31
47
|
@dataclass
|
|
@@ -34,8 +50,14 @@ class ContinueInterruptedPatternFlowStackFrame(PatternFlowStackFrame):
|
|
|
34
50
|
|
|
35
51
|
flow_id: str = FLOW_PATTERN_CONTINUE_INTERRUPTED
|
|
36
52
|
"""The ID of the flow."""
|
|
37
|
-
|
|
38
|
-
"""The
|
|
53
|
+
interrupted_flow_names: List[str] = field(default_factory=list)
|
|
54
|
+
"""The names of the previous flows that were interrupted."""
|
|
55
|
+
interrupted_flow_ids: List[str] = field(default_factory=list)
|
|
56
|
+
"""The ids of the previous flows that were interrupted."""
|
|
57
|
+
interrupted_flow_options: str = ""
|
|
58
|
+
"""The options that the user can choose from as a string."""
|
|
59
|
+
multiple_flows_interrupted: bool = False
|
|
60
|
+
"""Whether the user has interrupted multiple flows."""
|
|
39
61
|
|
|
40
62
|
@classmethod
|
|
41
63
|
def type(cls) -> str:
|
|
@@ -55,7 +77,10 @@ class ContinueInterruptedPatternFlowStackFrame(PatternFlowStackFrame):
|
|
|
55
77
|
return ContinueInterruptedPatternFlowStackFrame(
|
|
56
78
|
frame_id=data["frame_id"],
|
|
57
79
|
step_id=data["step_id"],
|
|
58
|
-
|
|
80
|
+
interrupted_flow_names=data["interrupted_flow_names"],
|
|
81
|
+
interrupted_flow_ids=data["interrupted_flow_ids"],
|
|
82
|
+
interrupted_flow_options=data["interrupted_flow_options"],
|
|
83
|
+
multiple_flows_interrupted=len(data["interrupted_flow_names"]) > 1,
|
|
59
84
|
)
|
|
60
85
|
|
|
61
86
|
def __eq__(self, other: Any) -> bool:
|
|
@@ -64,13 +89,15 @@ class ContinueInterruptedPatternFlowStackFrame(PatternFlowStackFrame):
|
|
|
64
89
|
return (
|
|
65
90
|
self.flow_id == other.flow_id
|
|
66
91
|
and self.step_id == other.step_id
|
|
67
|
-
and self.
|
|
92
|
+
and self.interrupted_flow_names == other.interrupted_flow_names
|
|
93
|
+
and self.interrupted_flow_ids == other.interrupted_flow_ids
|
|
94
|
+
and self.interrupted_flow_options == other.interrupted_flow_options
|
|
68
95
|
)
|
|
69
96
|
|
|
70
97
|
|
|
71
|
-
class
|
|
98
|
+
class ActionContinueInterruptedFlow(Action):
|
|
72
99
|
def name(self) -> str:
|
|
73
|
-
return
|
|
100
|
+
return ACTION_CONTINUE_INTERRUPTED_FLOW
|
|
74
101
|
|
|
75
102
|
async def run(
|
|
76
103
|
self,
|
|
@@ -79,25 +106,62 @@ class ActionSetInterruptedFlows(Action):
|
|
|
79
106
|
tracker: DialogueStateTracker,
|
|
80
107
|
domain: Domain,
|
|
81
108
|
metadata: Optional[Dict[Text, Any]] = None,
|
|
82
|
-
) ->
|
|
83
|
-
|
|
84
|
-
|
|
109
|
+
) -> List[Event]:
|
|
110
|
+
# get the pattern frame from the stack
|
|
111
|
+
pattern_frame = get_active_continue_interrupted_pattern_frame(tracker.stack)
|
|
112
|
+
|
|
113
|
+
if pattern_frame is None:
|
|
114
|
+
structlogger.warning(
|
|
115
|
+
"action.continue_interrupted_flows.no_continue_interrupted_frame"
|
|
116
|
+
)
|
|
117
|
+
return []
|
|
85
118
|
|
|
86
|
-
|
|
87
|
-
|
|
119
|
+
interrupted_flow_ids = pattern_frame.interrupted_flow_ids
|
|
120
|
+
interrupted_flow_names = pattern_frame.interrupted_flow_names
|
|
121
|
+
multiple_flows_interrupted = pattern_frame.multiple_flows_interrupted
|
|
122
|
+
|
|
123
|
+
flow_to_continue = None
|
|
124
|
+
if not multiple_flows_interrupted:
|
|
125
|
+
# the user confirmed that they want to continue the flow
|
|
126
|
+
# as only one flow was interrupted, we can just continue the first one
|
|
127
|
+
flow_to_continue = interrupted_flow_ids[0]
|
|
128
|
+
else:
|
|
129
|
+
# the user mentioned the flow they want to continue
|
|
130
|
+
# check if the flow is in the list of interrupted flows
|
|
131
|
+
selected_flow = tracker.get_slot(INTERRUPTED_FLOW_TO_CONTINUE_SLOT)
|
|
132
|
+
if selected_flow in interrupted_flow_ids:
|
|
133
|
+
flow_to_continue = selected_flow
|
|
134
|
+
elif selected_flow in interrupted_flow_names:
|
|
135
|
+
# the user mentioned the flow by name
|
|
136
|
+
# find the flow id for the flow name
|
|
137
|
+
# the list of names and ids are in the same order
|
|
138
|
+
flow_to_continue = interrupted_flow_ids[
|
|
139
|
+
interrupted_flow_names.index(selected_flow)
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
# if the user did not select a valid flow,
|
|
143
|
+
# we need to ask them to select a valid flow
|
|
144
|
+
if flow_to_continue is None:
|
|
145
|
+
await output_channel.send_text_message(
|
|
146
|
+
tracker.sender_id,
|
|
147
|
+
"You haven't selected a valid task to resume. "
|
|
148
|
+
"Please specify the task you would like to continue. "
|
|
149
|
+
"The options are: {{context.interrupted_flow_options}}",
|
|
150
|
+
)
|
|
151
|
+
return []
|
|
88
152
|
|
|
89
|
-
|
|
90
|
-
|
|
153
|
+
# resume the flow the user selected
|
|
154
|
+
events = resume_flow(flow_to_continue, tracker, tracker.stack)
|
|
91
155
|
|
|
92
|
-
return [
|
|
93
|
-
SlotSet(
|
|
94
|
-
SlotSet(
|
|
156
|
+
return events + [
|
|
157
|
+
SlotSet(INTERRUPTED_FLOW_TO_CONTINUE_SLOT, None),
|
|
158
|
+
SlotSet(CONTINUE_INTERRUPTED_FLOW_CONFIRMATION_SLOT, None),
|
|
95
159
|
]
|
|
96
160
|
|
|
97
161
|
|
|
98
|
-
class
|
|
162
|
+
class ActionCancelInterruptedFlows(Action):
|
|
99
163
|
def name(self) -> str:
|
|
100
|
-
return
|
|
164
|
+
return ACTION_CANCEL_INTERRUPTED_FLOWS
|
|
101
165
|
|
|
102
166
|
async def run(
|
|
103
167
|
self,
|
|
@@ -107,107 +171,114 @@ class ActionAskInterruptedFlowToContinue(Action):
|
|
|
107
171
|
domain: Domain,
|
|
108
172
|
metadata: Optional[Dict[Text, Any]] = None,
|
|
109
173
|
) -> List[Event]:
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
buttons = [
|
|
113
|
-
{
|
|
114
|
-
"title": flow_id,
|
|
115
|
-
"payload": f'/SetSlots{{"{INTERRUPTED_FLOW_TO_CONTINUE_SLOT}": '
|
|
116
|
-
f'"{flow_id}"}}',
|
|
117
|
-
}
|
|
118
|
-
for flow_id in interrupted_flows or []
|
|
119
|
-
]
|
|
120
|
-
buttons.append(
|
|
121
|
-
{
|
|
122
|
-
"title": "None of them",
|
|
123
|
-
"payload": f'/SetSlots{{"{INTERRUPTED_FLOW_TO_CONTINUE_SLOT}": '
|
|
124
|
-
f'"none"}}',
|
|
125
|
-
}
|
|
126
|
-
)
|
|
174
|
+
# get the pattern frame from the stack
|
|
175
|
+
pattern_frame = get_active_continue_interrupted_pattern_frame(tracker.stack)
|
|
127
176
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
)
|
|
177
|
+
if pattern_frame is None:
|
|
178
|
+
structlogger.warning(
|
|
179
|
+
"action.continue_interrupted_flows.no_continue_interrupted_frame"
|
|
180
|
+
)
|
|
181
|
+
return []
|
|
134
182
|
|
|
135
|
-
|
|
183
|
+
interrupted_flow_ids = pattern_frame.interrupted_flow_ids
|
|
136
184
|
|
|
185
|
+
event_list: List[Event] = []
|
|
137
186
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
187
|
+
# cancel all interrupted flows
|
|
188
|
+
for flow_id in interrupted_flow_ids:
|
|
189
|
+
event_list.extend(self.cancel_flow(tracker, tracker.stack, flow_id))
|
|
141
190
|
|
|
142
|
-
|
|
191
|
+
return event_list + [
|
|
192
|
+
SlotSet(INTERRUPTED_FLOW_TO_CONTINUE_SLOT, None),
|
|
193
|
+
SlotSet(CONTINUE_INTERRUPTED_FLOW_CONFIRMATION_SLOT, None),
|
|
194
|
+
]
|
|
195
|
+
|
|
196
|
+
def cancel_flow(
|
|
143
197
|
self,
|
|
144
|
-
output_channel: OutputChannel,
|
|
145
|
-
nlg: NaturalLanguageGenerator,
|
|
146
198
|
tracker: DialogueStateTracker,
|
|
147
|
-
|
|
148
|
-
|
|
199
|
+
stack: DialogueStack,
|
|
200
|
+
flow_id: str,
|
|
149
201
|
) -> List[Event]:
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
# get all necessary slot values
|
|
153
|
-
multiple = tracker.get_slot(MULTIPLE_FLOWS_INTERRUPTED_SLOT)
|
|
154
|
-
selected_flow = tracker.get_slot(INTERRUPTED_FLOW_TO_CONTINUE_SLOT)
|
|
155
|
-
interrupted_flows = tracker.get_slot(INTERRUPTED_FLOWS_SLOT) or []
|
|
202
|
+
"""Cancels a flow by flow id."""
|
|
203
|
+
applied_events: List[Event] = []
|
|
156
204
|
|
|
157
|
-
|
|
205
|
+
frames_to_cancel, user_frame_to_cancel = self._collect_frames_to_cancel(
|
|
206
|
+
stack, flow_id
|
|
207
|
+
)
|
|
158
208
|
|
|
159
|
-
#
|
|
160
|
-
if
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
flow_id = interrupted_flows[0] if interrupted_flows else None
|
|
165
|
-
|
|
166
|
-
event_list = []
|
|
167
|
-
if flow_id:
|
|
168
|
-
# TODO: refactor to avoid creating a StartFlowCommand first
|
|
169
|
-
# resume the flow with the provided flow id
|
|
170
|
-
start_flow_command = StartFlowCommand(flow_id)
|
|
171
|
-
event_list = start_flow_command.resume_flow(
|
|
172
|
-
tracker, tracker.stack, original_user_frame
|
|
173
|
-
)
|
|
174
|
-
else:
|
|
175
|
-
await output_channel.send_text_message(
|
|
176
|
-
tracker.sender_id,
|
|
177
|
-
"You haven't selected a valid task to resume. "
|
|
178
|
-
"Please specify the task you would like to continue.",
|
|
209
|
+
# if the flow is not on the stack, do nothing
|
|
210
|
+
if user_frame_to_cancel is None:
|
|
211
|
+
structlogger.error(
|
|
212
|
+
"cancel_flow.no_user_frame_to_cancel",
|
|
213
|
+
flow_id=flow_id,
|
|
179
214
|
)
|
|
215
|
+
return []
|
|
180
216
|
|
|
181
|
-
|
|
217
|
+
frames_ids_to_cancel = [frame.frame_id for frame in frames_to_cancel]
|
|
182
218
|
|
|
219
|
+
stack.push(
|
|
220
|
+
CancelPatternFlowStackFrame(
|
|
221
|
+
canceled_name=flow_id,
|
|
222
|
+
canceled_frames=frames_ids_to_cancel,
|
|
223
|
+
)
|
|
224
|
+
)
|
|
183
225
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
226
|
+
# create flow cancelled event
|
|
227
|
+
applied_events.extend(
|
|
228
|
+
[
|
|
229
|
+
FlowCancelled(
|
|
230
|
+
user_frame_to_cancel.flow_id, user_frame_to_cancel.step_id
|
|
231
|
+
),
|
|
232
|
+
]
|
|
233
|
+
)
|
|
234
|
+
# create agent cancelled events for any agent frames that are on the stack
|
|
235
|
+
for frame in frames_to_cancel:
|
|
236
|
+
if isinstance(frame, AgentStackFrame):
|
|
237
|
+
applied_events.append(
|
|
238
|
+
AgentCancelled(
|
|
239
|
+
frame.agent_id, frame.flow_id, reason="Flow was cancelled"
|
|
240
|
+
)
|
|
241
|
+
)
|
|
187
242
|
|
|
188
|
-
|
|
189
|
-
self,
|
|
190
|
-
output_channel: OutputChannel,
|
|
191
|
-
nlg: NaturalLanguageGenerator,
|
|
192
|
-
tracker: DialogueStateTracker,
|
|
193
|
-
domain: Domain,
|
|
194
|
-
metadata: Optional[Dict[Text, Any]] = None,
|
|
195
|
-
) -> List[Event]:
|
|
196
|
-
from rasa.dialogue_understanding.commands import CancelFlowCommand
|
|
243
|
+
update_stack_events = tracker.create_stack_updated_events(stack)
|
|
197
244
|
|
|
198
|
-
|
|
245
|
+
return applied_events + update_stack_events
|
|
199
246
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
event_list.extend(
|
|
205
|
-
cancel_flow_command.cancel_flow(tracker, tracker.stack, flow_id)
|
|
206
|
-
)
|
|
247
|
+
def _collect_frames_to_cancel(
|
|
248
|
+
self, stack: DialogueStack, target_flow_id: str
|
|
249
|
+
) -> Tuple[List[DialogueStackFrame], Optional[UserFlowStackFrame]]:
|
|
250
|
+
"""Collect frames that need to be cancelled.
|
|
207
251
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
252
|
+
Args:
|
|
253
|
+
stack: The stack to collect frames from.
|
|
254
|
+
target_flow_id: The ID of the flow to cancel.
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
A tuple containing (frames_to_cancel, frame_to_cancel).
|
|
258
|
+
"""
|
|
259
|
+
frames_to_cancel: List[DialogueStackFrame] = []
|
|
260
|
+
frame_found = False
|
|
261
|
+
frame_to_cancel = None
|
|
262
|
+
|
|
263
|
+
# collect all frames that belong to the target flow
|
|
264
|
+
# i.e. we want to cancel all frames that are on the stack and between
|
|
265
|
+
# the user flow frame that belongs to the target flow and the next user
|
|
266
|
+
# flow frame that belongs to a different flow
|
|
267
|
+
# this includes any pattern frames or agent frames as well
|
|
268
|
+
for frame in stack.frames:
|
|
269
|
+
if isinstance(frame, UserFlowStackFrame) and (
|
|
270
|
+
frame.frame_type == FlowStackFrameType.REGULAR
|
|
271
|
+
or frame.frame_type == FlowStackFrameType.INTERRUPT
|
|
272
|
+
):
|
|
273
|
+
if frame.flow_id == target_flow_id:
|
|
274
|
+
frames_to_cancel.append(frame)
|
|
275
|
+
frame_to_cancel = frame
|
|
276
|
+
frame_found = True
|
|
277
|
+
continue
|
|
278
|
+
elif frame_found:
|
|
279
|
+
break
|
|
280
|
+
|
|
281
|
+
if frame_found:
|
|
282
|
+
frames_to_cancel.append(frame)
|
|
283
|
+
|
|
284
|
+
return list(frames_to_cancel), frame_to_cancel
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
version: "3.1"
|
|
2
2
|
responses:
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
- text: "Would you like to continue with {{
|
|
4
|
+
utter_ask_continue_interrupted_flow_confirmation:
|
|
5
|
+
- text: "Would you like to continue with {{context.interrupted_flow_options}}?"
|
|
6
|
+
metadata:
|
|
7
|
+
rephrase: True
|
|
8
|
+
template: jinja
|
|
9
|
+
|
|
10
|
+
utter_ask_interrupted_flow_to_continue:
|
|
11
|
+
- text: "Would you like to resume {{context.interrupted_flow_options}}?"
|
|
6
12
|
metadata:
|
|
7
13
|
rephrase: True
|
|
8
14
|
template: jinja
|
|
9
|
-
buttons:
|
|
10
|
-
- title: "Yes"
|
|
11
|
-
payload: "/SetSlots(confirmation_continue_interrupted_flow=true)"
|
|
12
|
-
- title: "No"
|
|
13
|
-
payload: "/SetSlots(confirmation_continue_interrupted_flow=false)"
|
|
14
15
|
|
|
15
16
|
utter_ask_rephrase:
|
|
16
17
|
- text: I’m sorry I am unable to understand you, could you please rephrase?
|
|
@@ -129,19 +130,14 @@ slots:
|
|
|
129
130
|
type: float
|
|
130
131
|
initial_value: 0.0
|
|
131
132
|
max_value: 1000000
|
|
132
|
-
interrupted_flows:
|
|
133
|
-
type: list
|
|
134
|
-
multiple_flows_interrupted:
|
|
135
|
-
type: bool
|
|
136
|
-
initial_value: false
|
|
137
|
-
confirmation_continue_interrupted_flow:
|
|
138
|
-
type: bool
|
|
139
|
-
mappings:
|
|
140
|
-
- type: from_llm
|
|
141
133
|
interrupted_flow_to_continue:
|
|
142
134
|
type: text
|
|
143
135
|
mappings:
|
|
144
136
|
- type: from_llm
|
|
137
|
+
continue_interrupted_flow_confirmation:
|
|
138
|
+
type: bool
|
|
139
|
+
mappings:
|
|
140
|
+
- type: from_llm
|
|
145
141
|
|
|
146
142
|
flows:
|
|
147
143
|
pattern_cancel_flow:
|
|
@@ -228,10 +224,9 @@ flows:
|
|
|
228
224
|
description: Conversation repair flow for managing when users switch between different flows
|
|
229
225
|
name: pattern continue interrupted
|
|
230
226
|
steps:
|
|
231
|
-
- action: action_set_interrupted_flows
|
|
232
227
|
- noop: true
|
|
233
228
|
next:
|
|
234
|
-
- if:
|
|
229
|
+
- if: context.multiple_flows_interrupted
|
|
235
230
|
then:
|
|
236
231
|
- collect: interrupted_flow_to_continue
|
|
237
232
|
description: "Fill this slot with the name of the flow the user wants to continue. If the user does not want to continue any of the interrupted flows, fill this slot with 'none'."
|
|
@@ -241,19 +236,18 @@ flows:
|
|
|
241
236
|
- action: action_continue_interrupted_flow
|
|
242
237
|
next: END
|
|
243
238
|
- else:
|
|
244
|
-
- action:
|
|
239
|
+
- action: action_cancel_interrupted_flows
|
|
245
240
|
next: END
|
|
246
241
|
- else:
|
|
247
|
-
- collect:
|
|
242
|
+
- collect: continue_interrupted_flow_confirmation
|
|
248
243
|
description: "If the user wants to continue the interrupted flow, fill this slot with true. If the user does not want to continue the interrupted flow, fill this slot with false."
|
|
249
|
-
utter: utter_ask_continue_interrupted_flow
|
|
250
244
|
next:
|
|
251
|
-
- if: slots.
|
|
245
|
+
- if: slots.continue_interrupted_flow_confirmation
|
|
252
246
|
then:
|
|
253
247
|
- action: action_continue_interrupted_flow
|
|
254
248
|
next: END
|
|
255
249
|
- else:
|
|
256
|
-
- action:
|
|
250
|
+
- action: action_cancel_interrupted_flows
|
|
257
251
|
next: END
|
|
258
252
|
|
|
259
253
|
pattern_correction:
|
|
@@ -17,6 +17,9 @@ from rasa.shared.core.flows.steps.constants import END_STEP
|
|
|
17
17
|
from rasa.shared.core.flows.steps.continuation import ContinueFlowStep
|
|
18
18
|
|
|
19
19
|
if typing.TYPE_CHECKING:
|
|
20
|
+
from rasa.dialogue_understanding.patterns.continue_interrupted import (
|
|
21
|
+
ContinueInterruptedPatternFlowStackFrame,
|
|
22
|
+
)
|
|
20
23
|
from rasa.shared.core.trackers import DialogueStateTracker
|
|
21
24
|
|
|
22
25
|
|
|
@@ -171,10 +174,40 @@ def user_flows_on_the_stack(dialogue_stack: DialogueStack) -> Set[str]:
|
|
|
171
174
|
All user flows that are currently on the stack.
|
|
172
175
|
"""
|
|
173
176
|
return {
|
|
174
|
-
|
|
177
|
+
frame.flow_id
|
|
178
|
+
for frame in user_frames_on_the_stack(
|
|
179
|
+
dialogue_stack, ignore_call_and_link_frames=False
|
|
180
|
+
)
|
|
175
181
|
}
|
|
176
182
|
|
|
177
183
|
|
|
184
|
+
def user_frames_on_the_stack(
|
|
185
|
+
dialogue_stack: DialogueStack, ignore_call_and_link_frames: bool = True
|
|
186
|
+
) -> List[UserFlowStackFrame]:
|
|
187
|
+
"""Get all user frames that are currently on the stack.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
dialogue_stack: The dialogue stack.
|
|
191
|
+
ignore_call_and_link_frames: Whether to ignore user frames of type `call`
|
|
192
|
+
and `link`. By default, these frames are ignored.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
All user frames that are currently on the stack.
|
|
196
|
+
"""
|
|
197
|
+
return [
|
|
198
|
+
frame
|
|
199
|
+
for frame in dialogue_stack.frames
|
|
200
|
+
if isinstance(frame, UserFlowStackFrame)
|
|
201
|
+
and (
|
|
202
|
+
not ignore_call_and_link_frames
|
|
203
|
+
or (
|
|
204
|
+
frame.frame_type != FlowStackFrameType.CALL
|
|
205
|
+
and frame.frame_type != FlowStackFrameType.LINK
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
]
|
|
209
|
+
|
|
210
|
+
|
|
178
211
|
def end_top_user_flow(stack: DialogueStack) -> DialogueStack:
|
|
179
212
|
"""Ends all frames on top of the stack including the topmost user frame.
|
|
180
213
|
|
|
@@ -235,13 +268,19 @@ def get_collect_steps_excluding_ask_before_filling_for_active_flow(
|
|
|
235
268
|
|
|
236
269
|
def is_continue_interrupted_flow_active(stack: DialogueStack) -> bool:
|
|
237
270
|
"""Check if the continue interrupted flow is active."""
|
|
271
|
+
return get_active_continue_interrupted_pattern_frame(stack) is not None
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def get_active_continue_interrupted_pattern_frame(
|
|
275
|
+
stack: DialogueStack,
|
|
276
|
+
) -> Optional["ContinueInterruptedPatternFlowStackFrame"]:
|
|
238
277
|
from rasa.dialogue_understanding.patterns.continue_interrupted import (
|
|
239
278
|
ContinueInterruptedPatternFlowStackFrame,
|
|
240
279
|
)
|
|
241
280
|
|
|
242
281
|
for frame in reversed(stack.frames):
|
|
243
282
|
if isinstance(frame, ContinueInterruptedPatternFlowStackFrame):
|
|
244
|
-
return
|
|
283
|
+
return frame
|
|
245
284
|
if isinstance(frame, UserFlowStackFrame):
|
|
246
|
-
return
|
|
247
|
-
return
|
|
285
|
+
return None
|
|
286
|
+
return None
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import typing
|
|
1
2
|
from contextlib import contextmanager
|
|
2
3
|
from typing import Any, Dict, Generator, List, Optional, Text
|
|
3
4
|
|
|
4
5
|
import structlog
|
|
5
6
|
|
|
6
|
-
from rasa.dialogue_understanding.commands import Command, NoopCommand, SetSlotCommand
|
|
7
7
|
from rasa.dialogue_understanding.constants import (
|
|
8
8
|
RASA_RECORD_COMMANDS_AND_PROMPTS_ENV_VAR_NAME,
|
|
9
9
|
)
|
|
@@ -23,6 +23,9 @@ from rasa.shared.nlu.training_data.message import Message
|
|
|
23
23
|
from rasa.shared.providers.llm.llm_response import LLMResponse
|
|
24
24
|
from rasa.utils.common import get_bool_env_variable
|
|
25
25
|
|
|
26
|
+
if typing.TYPE_CHECKING:
|
|
27
|
+
from rasa.dialogue_understanding.commands import Command
|
|
28
|
+
|
|
26
29
|
record_commands_and_prompts = get_bool_env_variable(
|
|
27
30
|
RASA_RECORD_COMMANDS_AND_PROMPTS_ENV_VAR_NAME, False
|
|
28
31
|
)
|
|
@@ -41,7 +44,7 @@ def set_record_commands_and_prompts() -> Generator:
|
|
|
41
44
|
|
|
42
45
|
|
|
43
46
|
def add_commands_to_message_parse_data(
|
|
44
|
-
message: Message, component_name: str, commands: List[Command]
|
|
47
|
+
message: Message, component_name: str, commands: List["Command"]
|
|
45
48
|
) -> None:
|
|
46
49
|
"""Add commands to the message parse data.
|
|
47
50
|
|
|
@@ -144,6 +147,11 @@ def _handle_via_nlu_in_coexistence(
|
|
|
144
147
|
tracker: Optional[DialogueStateTracker], message: Message
|
|
145
148
|
) -> bool:
|
|
146
149
|
"""Check if the message should be handled by the NLU subsystem in coexistence mode.""" # noqa: E501
|
|
150
|
+
from rasa.dialogue_understanding.commands import (
|
|
151
|
+
NoopCommand,
|
|
152
|
+
SetSlotCommand,
|
|
153
|
+
)
|
|
154
|
+
|
|
147
155
|
if not tracker:
|
|
148
156
|
return False
|
|
149
157
|
|
|
@@ -156,8 +164,7 @@ def _handle_via_nlu_in_coexistence(
|
|
|
156
164
|
"utils.handle_via_nlu_in_coexistence"
|
|
157
165
|
".tracker_missing_route_session_to_calm_slot",
|
|
158
166
|
event_info=(
|
|
159
|
-
f"Tracker doesn't have the '{ROUTE_TO_CALM_SLOT}' slot."
|
|
160
|
-
f"Routing to CALM."
|
|
167
|
+
f"Tracker doesn't have the '{ROUTE_TO_CALM_SLOT}' slot.Routing to CALM."
|
|
161
168
|
),
|
|
162
169
|
route_session_to_calm=commands,
|
|
163
170
|
)
|
|
@@ -218,3 +225,16 @@ def _handle_via_nlu_in_coexistence(
|
|
|
218
225
|
commands=commands,
|
|
219
226
|
)
|
|
220
227
|
return False
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def assemble_options_string(names: List[str], conjunction: str = "and") -> str:
|
|
231
|
+
"""Concatenate options to a human-readable string."""
|
|
232
|
+
option_message = ""
|
|
233
|
+
for i, name in enumerate(names):
|
|
234
|
+
if i == 0:
|
|
235
|
+
option_message += name
|
|
236
|
+
elif i == len(names) - 1:
|
|
237
|
+
option_message += f" {conjunction} {name}"
|
|
238
|
+
else:
|
|
239
|
+
option_message += f", {name}"
|
|
240
|
+
return option_message
|
rasa/model_training.py
CHANGED
|
@@ -145,22 +145,23 @@ def _check_unresolved_slots(domain: Domain, stories: StoryGraph) -> None:
|
|
|
145
145
|
|
|
146
146
|
|
|
147
147
|
async def train(
|
|
148
|
-
domain:
|
|
149
|
-
config:
|
|
150
|
-
training_files: Optional[Union[
|
|
151
|
-
output:
|
|
148
|
+
domain: str,
|
|
149
|
+
config: str,
|
|
150
|
+
training_files: Optional[Union[str, List[str]]],
|
|
151
|
+
output: str = rasa.shared.constants.DEFAULT_MODELS_PATH,
|
|
152
152
|
dry_run: bool = False,
|
|
153
153
|
force_training: bool = False,
|
|
154
|
-
fixed_model_name: Optional[
|
|
154
|
+
fixed_model_name: Optional[str] = None,
|
|
155
155
|
persist_nlu_training_data: bool = False,
|
|
156
156
|
core_additional_arguments: Optional[Dict] = None,
|
|
157
157
|
nlu_additional_arguments: Optional[Dict] = None,
|
|
158
|
-
model_to_finetune: Optional[
|
|
158
|
+
model_to_finetune: Optional[str] = None,
|
|
159
159
|
finetuning_epoch_fraction: float = 1.0,
|
|
160
160
|
remote_storage: Optional[StorageType] = None,
|
|
161
161
|
file_importer: Optional[TrainingDataImporter] = None,
|
|
162
162
|
keep_local_model_copy: bool = False,
|
|
163
163
|
remote_root_only: bool = False,
|
|
164
|
+
sub_agents: Optional[str] = None,
|
|
164
165
|
) -> TrainingResult:
|
|
165
166
|
"""Trains a Rasa model (Core and NLU).
|
|
166
167
|
|
|
@@ -190,6 +191,7 @@ async def train(
|
|
|
190
191
|
remote storage is configured.
|
|
191
192
|
remote_root_only: If `True`, the model will be stored in the root of the
|
|
192
193
|
remote model storage.
|
|
194
|
+
sub_agents: Path to sub-agents directory.
|
|
193
195
|
|
|
194
196
|
Returns:
|
|
195
197
|
An instance of `TrainingResult`.
|
rasa/shared/constants.py
CHANGED
|
@@ -249,6 +249,9 @@ _VALIDATE_ENVIRONMENT_MISSING_KEYS_KEY = "missing_keys"
|
|
|
249
249
|
LLM_API_HEALTH_CHECK_ENV_VAR = "LLM_API_HEALTH_CHECK"
|
|
250
250
|
LLM_API_HEALTH_CHECK_DEFAULT_VALUE = "false"
|
|
251
251
|
|
|
252
|
+
MCP_API_HEALTH_CHECK_ENV_VAR = "MCP_API_HEALTH_CHECK"
|
|
253
|
+
MCP_API_HEALTH_CHECK_DEFAULT_VALUE = "false"
|
|
254
|
+
|
|
252
255
|
AWS_REGION_NAME_CONFIG_KEY = "aws_region_name"
|
|
253
256
|
AWS_ACCESS_KEY_ID_CONFIG_KEY = "aws_access_key_id"
|
|
254
257
|
AWS_SECRET_ACCESS_KEY_CONFIG_KEY = "aws_secret_access_key"
|