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.

Files changed (82) hide show
  1. rasa/agents/exceptions.py +31 -1
  2. rasa/agents/protocol/a2a/a2a_agent.py +1 -1
  3. rasa/agents/protocol/mcp/mcp_base_agent.py +7 -6
  4. rasa/agents/protocol/mcp/mcp_task_agent.py +1 -1
  5. rasa/agents/utils.py +5 -0
  6. rasa/agents/validation.py +484 -0
  7. rasa/api.py +9 -6
  8. rasa/cli/arguments/train.py +2 -0
  9. rasa/cli/interactive.py +2 -0
  10. rasa/cli/train.py +2 -0
  11. rasa/cli/utils.py +85 -1
  12. rasa/core/actions/action.py +2 -6
  13. rasa/core/available_agents.py +47 -26
  14. rasa/core/channels/inspector/dist/assets/{arc-63212852.js → arc-cce7e0a8.js} +1 -1
  15. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-eecf6b13.js → blockDiagram-38ab4fdb-e2a49be7.js} +1 -1
  16. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-8f798a9a.js → c4Diagram-3d4e48cf-3def7895.js} +1 -1
  17. rasa/core/channels/inspector/dist/assets/channel-858c2c20.js +1 -0
  18. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-df71a04c.js → classDiagram-70f12bd4-e66fe4df.js} +1 -1
  19. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-9b275968.js → classDiagram-v2-f2320105-eb874aaa.js} +1 -1
  20. rasa/core/channels/inspector/dist/assets/clone-4b80996c.js +1 -0
  21. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-1c669cad.js → createText-2e5e7dd3-cf934643.js} +1 -1
  22. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-b1553799.js → edges-e0da2a9e-8fdf9155.js} +1 -1
  23. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-112388d6.js → erDiagram-9861fffd-6106fb96.js} +1 -1
  24. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-fdebec47.js → flowDb-956e92f1-4c2bb040.js} +1 -1
  25. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-6280ede1.js → flowDiagram-66a62f08-f0ff96af.js} +1 -1
  26. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-16f09b7a.js +1 -0
  27. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-e1dc03e5.js → flowchart-elk-definition-4a651766-a21707ec.js} +1 -1
  28. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-83f68c51.js → ganttDiagram-c361ad54-c165acb1.js} +1 -1
  29. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-22f8666f.js → gitGraphDiagram-72cf32ee-b0564cf1.js} +1 -1
  30. rasa/core/channels/inspector/dist/assets/{graph-ca9e6217.js → graph-e557e67a.js} +1 -1
  31. rasa/core/channels/inspector/dist/assets/{index-3862675e-c5ceb692.js → index-3862675e-1ce60e9e.js} +1 -1
  32. rasa/core/channels/inspector/dist/assets/{index-3e293924.js → index-996fe816.js} +173 -173
  33. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-faa9999b.js → infoDiagram-f8f76790-893569e2.js} +1 -1
  34. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-c4dda8d9.js → journeyDiagram-49397b02-c29c864f.js} +1 -1
  35. rasa/core/channels/inspector/dist/assets/{layout-d4307784.js → layout-649a5eae.js} +1 -1
  36. rasa/core/channels/inspector/dist/assets/{line-0567aaa7.js → line-0e5685ed.js} +1 -1
  37. rasa/core/channels/inspector/dist/assets/{linear-c11b95cf.js → linear-eaa320bd.js} +1 -1
  38. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-0c7d3ca9.js → mindmap-definition-fc14e90a-f35df9e6.js} +1 -1
  39. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-34b433fa.js → pieDiagram-8a3498a8-78339e96.js} +1 -1
  40. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-4cab816e.js → quadrantDiagram-120e2f19-9b5f2f14.js} +1 -1
  41. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-8c22fa9e.js → requirementDiagram-deff3bca-d05ddb3a.js} +1 -1
  42. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-70ce9e8e.js → sankeyDiagram-04a897e0-d9be5dfd.js} +1 -1
  43. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-fbcd7fc9.js → sequenceDiagram-704730f1-0f1c4348.js} +1 -1
  44. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-45f05ea6.js → stateDiagram-587899a1-9ddf63b3.js} +1 -1
  45. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-beab1ea6.js → stateDiagram-v2-d93cdb3a-bc2b81ed.js} +1 -1
  46. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-2f29dbd5.js → styles-6aaf32cf-0a287936.js} +1 -1
  47. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-951eac83.js → styles-9a916d00-e3941990.js} +1 -1
  48. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-897fbfdd.js → styles-c10674c1-ce4eca24.js} +1 -1
  49. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-d667fac1.js → svgDrawCommon-08f97a94-d822b1a8.js} +1 -1
  50. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-e3205144.js → timeline-definition-85554ec2-e144c7a7.js} +1 -1
  51. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-4abeb0e2.js → xychartDiagram-e933f94c-ab7f4e14.js} +1 -1
  52. rasa/core/channels/inspector/dist/index.html +1 -1
  53. rasa/core/channels/inspector/src/App.tsx +28 -4
  54. rasa/core/channels/inspector/src/components/DialogueAgentStack.tsx +108 -0
  55. rasa/core/channels/inspector/src/components/{DialogueStack.tsx → DialogueHistoryStack.tsx} +7 -8
  56. rasa/core/channels/inspector/src/helpers/formatters.test.ts +4 -0
  57. rasa/core/channels/inspector/src/helpers/utils.test.ts +127 -0
  58. rasa/core/channels/inspector/src/helpers/utils.ts +66 -1
  59. rasa/core/channels/inspector/src/types.ts +17 -0
  60. rasa/core/policies/flows/flow_executor.py +52 -31
  61. rasa/dialogue_understanding/commands/cancel_flow_command.py +2 -81
  62. rasa/dialogue_understanding/commands/start_flow_command.py +18 -113
  63. rasa/dialogue_understanding/commands/utils.py +118 -0
  64. rasa/dialogue_understanding/patterns/clarify.py +3 -14
  65. rasa/dialogue_understanding/patterns/continue_interrupted.py +185 -114
  66. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +17 -23
  67. rasa/dialogue_understanding/stack/utils.py +43 -4
  68. rasa/dialogue_understanding/utils.py +24 -4
  69. rasa/model_training.py +8 -6
  70. rasa/shared/constants.py +3 -0
  71. rasa/shared/core/constants.py +5 -6
  72. rasa/shared/utils/health_check/health_check.py +7 -3
  73. rasa/shared/utils/mcp/server_connection.py +26 -6
  74. rasa/version.py +1 -1
  75. {rasa_pro-3.14.0.dev6.dist-info → rasa_pro-3.14.0.dev7.dist-info}/METADATA +1 -1
  76. {rasa_pro-3.14.0.dev6.dist-info → rasa_pro-3.14.0.dev7.dist-info}/RECORD +79 -76
  77. rasa/core/channels/inspector/dist/assets/channel-0cd70adf.js +0 -1
  78. rasa/core/channels/inspector/dist/assets/clone-a0f9c4ed.js +0 -1
  79. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-de9cc4aa.js +0 -1
  80. {rasa_pro-3.14.0.dev6.dist-info → rasa_pro-3.14.0.dev7.dist-info}/NOTICE +0 -0
  81. {rasa_pro-3.14.0.dev6.dist-info → rasa_pro-3.14.0.dev7.dist-info}/WHEEL +0 -0
  82. {rasa_pro-3.14.0.dev6.dist-info → rasa_pro-3.14.0.dev7.dist-info}/entry_points.txt +0 -0
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import re
4
4
  from dataclasses import dataclass
5
- from typing import Any, Dict, List, Optional, Tuple
5
+ from typing import Any, Dict, List
6
6
 
7
7
  import structlog
8
8
 
@@ -16,7 +16,6 @@ from rasa.dialogue_understanding.commands.command_syntax_manager import (
16
16
  )
17
17
  from rasa.dialogue_understanding.patterns.cancel import CancelPatternFlowStackFrame
18
18
  from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack
19
- from rasa.dialogue_understanding.stack.frames import DialogueStackFrame
20
19
  from rasa.dialogue_understanding.stack.frames.flow_stack_frame import (
21
20
  FlowStackFrameType,
22
21
  UserFlowStackFrame,
@@ -74,8 +73,7 @@ class CancelFlowCommand(Command):
74
73
  # we should never get here as we should always find the user flow
75
74
  # that was canceled.
76
75
  raise ValueError(
77
- f"Could not find a user flow frame to cancel. "
78
- f"Current stack: {stack}."
76
+ f"Could not find a user flow frame to cancel. Current stack: {stack}."
79
77
  )
80
78
 
81
79
  def run_command_on_tracker(
@@ -175,80 +173,3 @@ class CancelFlowCommand(Command):
175
173
  CommandSyntaxManager.get_syntax_version(),
176
174
  mapper[CommandSyntaxManager.get_default_syntax_version()],
177
175
  )
178
-
179
- def cancel_flow(
180
- self,
181
- tracker: DialogueStateTracker,
182
- stack: DialogueStack,
183
- flow_id: str,
184
- ) -> List[Event]:
185
- """Cancels a flow by flow id."""
186
- applied_events: List[Event] = []
187
-
188
- frames_to_cancel, user_frame_to_cancel = self._collect_frames_to_cancel(
189
- stack, flow_id
190
- )
191
-
192
- # if the flow is not on the stack, do nothing
193
- if user_frame_to_cancel is None:
194
- structlogger.error(
195
- "cancel_flow_command.cancel_flow.no_user_frame_to_cancel",
196
- command=self,
197
- )
198
- return []
199
-
200
- frames_ids_to_cancel = [frame.frame_id for frame in frames_to_cancel]
201
-
202
- stack.push(
203
- CancelPatternFlowStackFrame(
204
- canceled_name=flow_id,
205
- canceled_frames=frames_ids_to_cancel,
206
- )
207
- )
208
-
209
- # create flow cancelled event
210
- applied_events.extend(
211
- [
212
- FlowCancelled(
213
- user_frame_to_cancel.flow_id, user_frame_to_cancel.step_id
214
- ),
215
- ]
216
- )
217
-
218
- update_stack_events = tracker.create_stack_updated_events(stack)
219
-
220
- return applied_events + update_stack_events
221
-
222
- def _collect_frames_to_cancel(
223
- self, stack: DialogueStack, target_flow_id: str
224
- ) -> Tuple[List[DialogueStackFrame], Optional[UserFlowStackFrame]]:
225
- """Collect frames that need to be cancelled.
226
-
227
- Args:
228
- stack: The stack to collect frames from.
229
- target_flow_id: The ID of the flow to cancel.
230
-
231
- Returns:
232
- A tuple containing (frames_to_cancel, frame_to_cancel).
233
- """
234
- frames_to_cancel: List[DialogueStackFrame] = []
235
- frame_found = False
236
- frame_to_cancel = None
237
-
238
- for frame in stack.frames:
239
- if isinstance(frame, UserFlowStackFrame) and (
240
- frame.frame_type == FlowStackFrameType.REGULAR
241
- or frame.frame_type == FlowStackFrameType.INTERRUPT
242
- ):
243
- if frame.flow_id == target_flow_id:
244
- frames_to_cancel.append(frame)
245
- frame_to_cancel = frame
246
- frame_found = True
247
- continue
248
- elif frame_found:
249
- break
250
-
251
- if frame_found:
252
- frames_to_cancel.append(frame)
253
-
254
- return list(frames_to_cancel), frame_to_cancel
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import re
4
4
  from dataclasses import dataclass
5
- from typing import Any, Dict, List, Optional, Tuple
5
+ from typing import Any, Dict, List, Optional
6
6
 
7
7
  import structlog
8
8
 
@@ -11,17 +11,15 @@ from rasa.dialogue_understanding.commands.command_syntax_manager import (
11
11
  CommandSyntaxManager,
12
12
  CommandSyntaxVersion,
13
13
  )
14
- from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack
15
- from rasa.dialogue_understanding.stack.frames.dialogue_stack_frame import (
16
- DialogueStackFrame,
14
+ from rasa.dialogue_understanding.commands.utils import (
15
+ remove_pattern_continue_interrupted_frames,
16
+ resume_flow,
17
17
  )
18
18
  from rasa.dialogue_understanding.stack.frames.flow_stack_frame import (
19
- AgentStackFrame,
20
19
  AgentState,
21
20
  FlowStackFrameType,
22
21
  UserFlowStackFrame,
23
22
  )
24
- from rasa.dialogue_understanding.stack.frames.pattern_frame import PatternFlowStackFrame
25
23
  from rasa.dialogue_understanding.stack.utils import (
26
24
  is_continue_interrupted_flow_active,
27
25
  top_user_flow_frame,
@@ -29,10 +27,8 @@ from rasa.dialogue_understanding.stack.utils import (
29
27
  )
30
28
  from rasa.shared.core.events import (
31
29
  AgentInterrupted,
32
- AgentResumed,
33
30
  Event,
34
31
  FlowInterrupted,
35
- FlowResumed,
36
32
  )
37
33
  from rasa.shared.core.flows import FlowsList
38
34
  from rasa.shared.core.trackers import DialogueStateTracker
@@ -108,7 +104,7 @@ class StartFlowCommand(Command):
108
104
  # predicted a start flow command for the flow which is on top of the stack,
109
105
  # we just need to remove the pattern_continue_interrupted frame(s) from the
110
106
  # stack
111
- stack = _remove_pattern_continue_interrupted_frames(stack)
107
+ stack = remove_pattern_continue_interrupted_frames(stack)
112
108
  return applied_events + tracker.create_stack_updated_events(stack)
113
109
 
114
110
  # if the flow is already on the stack, resume it
@@ -118,11 +114,22 @@ class StartFlowCommand(Command):
118
114
  ):
119
115
  # if pattern_continue_interrupted is active, we need to remove it
120
116
  # from the stack before resuming the flow
121
- stack = _remove_pattern_continue_interrupted_frames(stack)
122
- return self.resume_flow(tracker, stack, original_user_frame)
117
+ stack = remove_pattern_continue_interrupted_frames(stack)
118
+ applied_events.extend(resume_flow(self.flow, tracker, stack))
119
+ # the current active flow is interrupted
120
+ applied_events.append(
121
+ FlowInterrupted(
122
+ original_user_frame.flow_id, original_user_frame.step_id
123
+ )
124
+ )
125
+ return applied_events
123
126
 
124
127
  frame_type = FlowStackFrameType.REGULAR
125
128
 
129
+ # remove the pattern_continue_interrupted frames from the stack
130
+ # if it is currently active but the user digressed from the pattern
131
+ stack = remove_pattern_continue_interrupted_frames(stack)
132
+
126
133
  if original_top_flow:
127
134
  # if the original top flow is not the same as the flow to start,
128
135
  # interrupt the current active flow
@@ -198,105 +205,3 @@ class StartFlowCommand(Command):
198
205
  CommandSyntaxManager.get_syntax_version(),
199
206
  mapper[CommandSyntaxManager.get_default_syntax_version()],
200
207
  )
201
-
202
- def resume_flow(
203
- self,
204
- tracker: DialogueStateTracker,
205
- stack: DialogueStack,
206
- original_user_frame: UserFlowStackFrame,
207
- ) -> List[Event]:
208
- """Resumes a flow by reordering frames."""
209
- applied_events: List[Event] = []
210
-
211
- # Resume existing flow by reordering frames
212
- frames_to_resume, user_frame_to_resume = self._collect_frames_to_resume(
213
- stack, self.flow
214
- )
215
-
216
- # if the flow is not on the stack, do nothing
217
- # this should not happen, but just in case
218
- if user_frame_to_resume is None:
219
- structlogger.error(
220
- "start_flow_command.resume_flow.no_user_frame_to_resume",
221
- command=self,
222
- )
223
- return []
224
-
225
- # move the frames to the top of the stack, e.g. reorder the frames
226
- # on the stack
227
- stack.move_frames_to_top(frames_to_resume)
228
- agent_stack_frame = next(
229
- (frame for frame in frames_to_resume if isinstance(frame, AgentStackFrame)),
230
- None,
231
- )
232
- if agent_stack_frame:
233
- agent_id = agent_stack_frame.agent_id
234
- applied_events.append(AgentResumed(agent_id, agent_stack_frame.flow_id))
235
-
236
- # Create flow interruption and resumption events
237
- applied_events.extend(
238
- [
239
- # the current active flow is interrupted
240
- FlowInterrupted(
241
- original_user_frame.flow_id, original_user_frame.step_id
242
- ),
243
- # the flow, which was on the stack, is resumed
244
- FlowResumed(user_frame_to_resume.flow_id, user_frame_to_resume.step_id),
245
- ]
246
- )
247
-
248
- return applied_events + tracker.create_stack_updated_events(stack)
249
-
250
- def _collect_frames_to_resume(
251
- self, stack: DialogueStack, target_flow_id: str
252
- ) -> Tuple[List[DialogueStackFrame], Optional[UserFlowStackFrame]]:
253
- """Collect frames that need to be resumed for the target flow.
254
-
255
- Args:
256
- stack: The stack to collect frames from.
257
- target_flow_id: The ID of the flow to resume.
258
-
259
- Returns:
260
- A tuple containing (frames_to_resume, frame_to_resume).
261
- """
262
- frames_to_resume: List[DialogueStackFrame] = []
263
- frame_found = False
264
- frame_to_resume = None
265
-
266
- for frame in stack.frames:
267
- if isinstance(frame, UserFlowStackFrame) and (
268
- frame.frame_type == FlowStackFrameType.REGULAR
269
- or frame.frame_type == FlowStackFrameType.INTERRUPT
270
- ):
271
- if frame.flow_id == target_flow_id:
272
- frames_to_resume.append(frame)
273
- frame_to_resume = frame
274
- frame_found = True
275
- continue
276
- elif frame_found:
277
- break
278
-
279
- if frame_found:
280
- frames_to_resume.append(frame)
281
-
282
- return list(frames_to_resume), frame_to_resume
283
-
284
-
285
- def _remove_pattern_continue_interrupted_frames(stack: DialogueStack) -> DialogueStack:
286
- """Remove pattern_continue_interrupted frames from the stack."""
287
- if not is_continue_interrupted_flow_active(stack):
288
- return stack
289
-
290
- # remove pattern_continue_interrupted from the stack
291
- top_frame = stack.top()
292
- while isinstance(top_frame, PatternFlowStackFrame):
293
- # If the top frame is a pattern frame, we need to remove it
294
- # before continuing with the active user flow frame.
295
- # This prevents the pattern frame
296
- # from being left on the stack when the flow is started
297
- # which would prevent pattern_completed to be triggered
298
- # once the user flow is completed.
299
- stack.pop()
300
- top_frame = stack.top()
301
-
302
- return stack
@@ -5,9 +5,21 @@ import structlog
5
5
  from rasa.dialogue_understanding.patterns.validate_slot import (
6
6
  ValidateSlotPatternFlowStackFrame,
7
7
  )
8
+ from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack
9
+ from rasa.dialogue_understanding.stack.frames.dialogue_stack_frame import (
10
+ DialogueStackFrame,
11
+ )
12
+ from rasa.dialogue_understanding.stack.frames.flow_stack_frame import (
13
+ AgentStackFrame,
14
+ FlowStackFrameType,
15
+ UserFlowStackFrame,
16
+ )
17
+ from rasa.dialogue_understanding.stack.frames.pattern_frame import PatternFlowStackFrame
8
18
  from rasa.shared.constants import ACTION_ASK_PREFIX, UTTER_ASK_PREFIX
9
19
  from rasa.shared.core.events import (
20
+ AgentResumed,
10
21
  Event,
22
+ FlowResumed,
11
23
  SlotSet,
12
24
  )
13
25
  from rasa.shared.core.flows import FlowsList
@@ -154,3 +166,109 @@ def find_default_flows_collecting_slot(
154
166
  for step in flow.get_collect_steps()
155
167
  )
156
168
  ]
169
+
170
+
171
+ def resume_flow(
172
+ flow_to_resume: str,
173
+ tracker: DialogueStateTracker,
174
+ stack: DialogueStack,
175
+ ) -> List[Event]:
176
+ """Resumes a flow by reordering frames."""
177
+ applied_events: List[Event] = []
178
+
179
+ # Resume existing flow by reordering frames
180
+ frames_to_resume, user_frame_to_resume = collect_frames_to_resume(
181
+ stack, flow_to_resume
182
+ )
183
+
184
+ # if the flow is not on the stack, do nothing
185
+ # this should not happen, but just in case
186
+ if user_frame_to_resume is None:
187
+ structlogger.error(
188
+ "resume_flow.no_user_frame_to_resume",
189
+ flow_to_resume=flow_to_resume,
190
+ )
191
+ return []
192
+
193
+ # move the frames to the top of the stack, e.g. reorder the frames
194
+ # on the stack
195
+ stack.move_frames_to_top(frames_to_resume)
196
+
197
+ # create agent resumed events if the agent frame is now on top of the stack
198
+ agent_stack_frame = next(
199
+ (frame for frame in frames_to_resume if isinstance(frame, AgentStackFrame)),
200
+ None,
201
+ )
202
+ if agent_stack_frame:
203
+ agent_id = agent_stack_frame.agent_id
204
+ applied_events.append(AgentResumed(agent_id, agent_stack_frame.flow_id))
205
+
206
+ # Create flow interruption and resumption events
207
+ applied_events.extend(
208
+ [
209
+ # the flow, which was on the stack, is resumed
210
+ FlowResumed(user_frame_to_resume.flow_id, user_frame_to_resume.step_id),
211
+ ]
212
+ )
213
+
214
+ return applied_events + tracker.create_stack_updated_events(stack)
215
+
216
+
217
+ def collect_frames_to_resume(
218
+ stack: DialogueStack,
219
+ target_flow_id: str, # pyright: ignore[reportUndefinedVariable]
220
+ ) -> Tuple[List[DialogueStackFrame], Optional[UserFlowStackFrame]]:
221
+ """Collect frames that need to be resumed for the target flow.
222
+
223
+ Args:
224
+ stack: The stack to collect frames from.
225
+ target_flow_id: The ID of the flow to resume.
226
+
227
+ Returns:
228
+ A tuple containing (frames_to_resume, frame_to_resume).
229
+ """
230
+ frames_to_resume: List[DialogueStackFrame] = []
231
+ frame_found = False
232
+ frame_to_resume = None
233
+
234
+ for frame in stack.frames:
235
+ if isinstance(frame, UserFlowStackFrame) and (
236
+ frame.frame_type == FlowStackFrameType.REGULAR
237
+ or frame.frame_type == FlowStackFrameType.INTERRUPT
238
+ ):
239
+ if frame.flow_id == target_flow_id:
240
+ frames_to_resume.append(frame)
241
+ frame_to_resume = frame
242
+ frame_found = True
243
+ continue
244
+ elif frame_found:
245
+ break
246
+
247
+ if frame_found:
248
+ frames_to_resume.append(frame)
249
+
250
+ return list(frames_to_resume), frame_to_resume
251
+
252
+
253
+ def remove_pattern_continue_interrupted_frames(stack: DialogueStack) -> DialogueStack:
254
+ """Remove pattern_continue_interrupted frames from the stack."""
255
+ from rasa.dialogue_understanding.stack.utils import (
256
+ is_continue_interrupted_flow_active,
257
+ )
258
+
259
+ if not is_continue_interrupted_flow_active(stack):
260
+ return stack
261
+
262
+ # remove pattern_continue_interrupted from the stack
263
+ top_frame = stack.top()
264
+ while isinstance(top_frame, PatternFlowStackFrame):
265
+ # If the top frame is a pattern frame, we need to remove it
266
+ # before continuing with the active user flow frame.
267
+ # This prevents the pattern frame
268
+ # from being left on the stack when the flow is started
269
+ # which would prevent pattern_completed to be triggered
270
+ # once the user flow is completed.
271
+ stack.pop()
272
+ top_frame = stack.top()
273
+
274
+ return stack
@@ -61,19 +61,6 @@ class ActionClarifyFlows(action.Action):
61
61
  """Return the flow name."""
62
62
  return ACTION_CLARIFY_FLOWS
63
63
 
64
- @staticmethod
65
- def assemble_options_string(names: List[str]) -> str:
66
- """Concatenate options to a human-readable string."""
67
- clarification_message = ""
68
- for i, name in enumerate(names):
69
- if i == 0:
70
- clarification_message += name
71
- elif i == len(names) - 1:
72
- clarification_message += f" or {name}"
73
- else:
74
- clarification_message += f", {name}"
75
- return clarification_message
76
-
77
64
  async def run(
78
65
  self,
79
66
  output_channel: "OutputChannel",
@@ -83,6 +70,8 @@ class ActionClarifyFlows(action.Action):
83
70
  metadata: Optional[Dict[str, Any]] = None,
84
71
  ) -> List[Event]:
85
72
  """Correct the slots."""
73
+ from rasa.dialogue_understanding.utils import assemble_options_string
74
+
86
75
  stack = tracker.stack
87
76
  if not (top := stack.top()):
88
77
  structlogger.warning("action.clarify_flows.no_active_flow")
@@ -92,7 +81,7 @@ class ActionClarifyFlows(action.Action):
92
81
  structlogger.warning("action.clarify_flows.no_clarification_frame")
93
82
  return []
94
83
 
95
- options_string = self.assemble_options_string(top.names)
84
+ options_string = assemble_options_string(top.names, conjunction="or")
96
85
  top.clarification_options = options_string
97
86
  # since we modified the stack frame, we need to update the stack
98
87
  return tracker.create_stack_updated_events(stack)