rasa-pro 3.13.7__py3-none-any.whl → 3.14.0.dev2__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 (179) hide show
  1. rasa/agents/__init__.py +0 -0
  2. rasa/agents/agent_factory.py +122 -0
  3. rasa/agents/agent_manager.py +162 -0
  4. rasa/agents/constants.py +31 -0
  5. rasa/agents/core/__init__.py +0 -0
  6. rasa/agents/core/agent_protocol.py +108 -0
  7. rasa/agents/core/types.py +70 -0
  8. rasa/agents/exceptions.py +8 -0
  9. rasa/agents/protocol/__init__.py +5 -0
  10. rasa/agents/protocol/a2a/__init__.py +0 -0
  11. rasa/agents/protocol/a2a/a2a_agent.py +51 -0
  12. rasa/agents/protocol/mcp/__init__.py +0 -0
  13. rasa/agents/protocol/mcp/mcp_base_agent.py +697 -0
  14. rasa/agents/protocol/mcp/mcp_open_agent.py +275 -0
  15. rasa/agents/protocol/mcp/mcp_task_agent.py +447 -0
  16. rasa/agents/schemas/__init__.py +6 -0
  17. rasa/agents/schemas/agent_input.py +24 -0
  18. rasa/agents/schemas/agent_output.py +26 -0
  19. rasa/agents/schemas/agent_tool_result.py +51 -0
  20. rasa/agents/schemas/agent_tool_schema.py +112 -0
  21. rasa/agents/templates/__init__.py +0 -0
  22. rasa/agents/templates/mcp_open_agent_prompt_template.jinja2 +15 -0
  23. rasa/agents/templates/mcp_task_agent_prompt_template.jinja2 +13 -0
  24. rasa/agents/utils.py +72 -0
  25. rasa/api.py +5 -0
  26. rasa/cli/arguments/default_arguments.py +12 -0
  27. rasa/cli/arguments/run.py +2 -0
  28. rasa/cli/dialogue_understanding_test.py +4 -0
  29. rasa/cli/e2e_test.py +4 -0
  30. rasa/cli/inspect.py +3 -0
  31. rasa/cli/llm_fine_tuning.py +5 -0
  32. rasa/cli/run.py +4 -0
  33. rasa/cli/shell.py +3 -0
  34. rasa/cli/train.py +2 -2
  35. rasa/constants.py +6 -0
  36. rasa/core/actions/action.py +69 -39
  37. rasa/core/actions/action_run_slot_rejections.py +1 -1
  38. rasa/core/agent.py +16 -0
  39. rasa/core/available_agents.py +196 -0
  40. rasa/core/available_endpoints.py +30 -0
  41. rasa/core/channels/development_inspector.py +47 -14
  42. rasa/core/channels/inspector/dist/assets/{arc-0b11fe30.js → arc-2e78c586.js} +1 -1
  43. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-9eef30a7.js → blockDiagram-38ab4fdb-806b712e.js} +1 -1
  44. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-03e94f28.js → c4Diagram-3d4e48cf-0745efa9.js} +1 -1
  45. rasa/core/channels/inspector/dist/assets/channel-c436ca7c.js +1 -0
  46. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-95c09eba.js → classDiagram-70f12bd4-7bd1082b.js} +1 -1
  47. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-38e8446c.js → classDiagram-v2-f2320105-d937ba49.js} +1 -1
  48. rasa/core/channels/inspector/dist/assets/clone-50dd656b.js +1 -0
  49. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-57dc3038.js → createText-2e5e7dd3-a2a564ca.js} +1 -1
  50. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-4bac0545.js → edges-e0da2a9e-b5256940.js} +1 -1
  51. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-81795c90.js → erDiagram-9861fffd-e6883ad2.js} +1 -1
  52. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-89489ae6.js → flowDb-956e92f1-e576fc02.js} +1 -1
  53. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-cd152627.js → flowDiagram-66a62f08-2e298d01.js} +1 -1
  54. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-2b2aeaf8.js +1 -0
  55. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-3da369bc.js → flowchart-elk-definition-4a651766-dd7b150a.js} +1 -1
  56. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-85ec16f8.js → ganttDiagram-c361ad54-5b79575c.js} +1 -1
  57. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-495bc140.js → gitGraphDiagram-72cf32ee-3016f40a.js} +1 -1
  58. rasa/core/channels/inspector/dist/assets/{graph-1ec4d266.js → graph-3e19170f.js} +1 -1
  59. rasa/core/channels/inspector/dist/assets/index-1bd9135e.js +1353 -0
  60. rasa/core/channels/inspector/dist/assets/{index-3862675e-0a0e97c9.js → index-3862675e-eb9c86de.js} +1 -1
  61. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-4d54bcde.js → infoDiagram-f8f76790-b4280e4d.js} +1 -1
  62. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-dc097114.js → journeyDiagram-49397b02-556091f8.js} +1 -1
  63. rasa/core/channels/inspector/dist/assets/{layout-1a08981e.js → layout-08436411.js} +1 -1
  64. rasa/core/channels/inspector/dist/assets/{line-95f7f1d3.js → line-683c4f3b.js} +1 -1
  65. rasa/core/channels/inspector/dist/assets/{linear-97e69543.js → linear-cee6d791.js} +1 -1
  66. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-8c71ff03.js → mindmap-definition-fc14e90a-a0bf0b1a.js} +1 -1
  67. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-f14c71c7.js → pieDiagram-8a3498a8-3730d5c4.js} +1 -1
  68. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-f1d3c9ff.js → quadrantDiagram-120e2f19-12a20fed.js} +1 -1
  69. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-bfa2412f.js → requirementDiagram-deff3bca-b9732102.js} +1 -1
  70. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-53f2c97b.js → sankeyDiagram-04a897e0-a2e72776.js} +1 -1
  71. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-319d7c0e.js → sequenceDiagram-704730f1-8b7a76bb.js} +1 -1
  72. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-76a09418.js → stateDiagram-587899a1-e65853ac.js} +1 -1
  73. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-a67f15d4.js → stateDiagram-v2-d93cdb3a-6f58a44b.js} +1 -1
  74. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-0654e7c3.js → styles-6aaf32cf-df25b934.js} +1 -1
  75. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-1394bb9d.js → styles-9a916d00-88357141.js} +1 -1
  76. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-e4c5bdae.js → styles-c10674c1-d600174d.js} +1 -1
  77. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-50957104.js → svgDrawCommon-08f97a94-4adc3e0b.js} +1 -1
  78. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-b0885a6a.js → timeline-definition-85554ec2-42816fa1.js} +1 -1
  79. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-79e6541a.js → xychartDiagram-e933f94c-621eb66a.js} +1 -1
  80. rasa/core/channels/inspector/dist/index.html +2 -2
  81. rasa/core/channels/inspector/index.html +1 -1
  82. rasa/core/channels/inspector/src/App.tsx +53 -7
  83. rasa/core/channels/inspector/src/components/Chat.tsx +3 -2
  84. rasa/core/channels/inspector/src/components/DiagramFlow.tsx +1 -1
  85. rasa/core/channels/inspector/src/components/DialogueStack.tsx +7 -5
  86. rasa/core/channels/inspector/src/components/LatencyDisplay.tsx +268 -0
  87. rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +6 -2
  88. rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +8 -3
  89. rasa/core/channels/inspector/src/helpers/formatters.ts +24 -3
  90. rasa/core/channels/inspector/src/theme/base/styles.ts +19 -1
  91. rasa/core/channels/inspector/src/types.ts +12 -0
  92. rasa/core/channels/studio_chat.py +125 -34
  93. rasa/core/channels/voice_ready/twilio_voice.py +1 -1
  94. rasa/core/channels/voice_stream/audiocodes.py +9 -6
  95. rasa/core/channels/voice_stream/browser_audio.py +39 -4
  96. rasa/core/channels/voice_stream/call_state.py +13 -2
  97. rasa/core/channels/voice_stream/genesys.py +16 -13
  98. rasa/core/channels/voice_stream/jambonz.py +13 -11
  99. rasa/core/channels/voice_stream/twilio_media_streams.py +14 -13
  100. rasa/core/channels/voice_stream/util.py +11 -1
  101. rasa/core/channels/voice_stream/voice_channel.py +101 -29
  102. rasa/core/constants.py +4 -0
  103. rasa/core/nlg/contextual_response_rephraser.py +11 -7
  104. rasa/core/nlg/generator.py +21 -5
  105. rasa/core/nlg/response.py +43 -6
  106. rasa/core/nlg/translate.py +8 -0
  107. rasa/core/policies/enterprise_search_policy.py +4 -2
  108. rasa/core/policies/flow_policy.py +2 -2
  109. rasa/core/policies/flows/flow_executor.py +374 -35
  110. rasa/core/policies/flows/mcp_tool_executor.py +240 -0
  111. rasa/core/processor.py +6 -1
  112. rasa/core/run.py +8 -1
  113. rasa/core/utils.py +21 -1
  114. rasa/dialogue_understanding/commands/__init__.py +8 -0
  115. rasa/dialogue_understanding/commands/cancel_flow_command.py +97 -4
  116. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +11 -0
  117. rasa/dialogue_understanding/commands/clarify_command.py +10 -0
  118. rasa/dialogue_understanding/commands/continue_agent_command.py +91 -0
  119. rasa/dialogue_understanding/commands/knowledge_answer_command.py +11 -0
  120. rasa/dialogue_understanding/commands/restart_agent_command.py +162 -0
  121. rasa/dialogue_understanding/commands/start_flow_command.py +129 -8
  122. rasa/dialogue_understanding/commands/utils.py +6 -2
  123. rasa/dialogue_understanding/generator/command_parser.py +4 -0
  124. rasa/dialogue_understanding/generator/llm_based_command_generator.py +50 -12
  125. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +61 -0
  126. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +61 -0
  127. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +81 -0
  128. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +81 -0
  129. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +7 -6
  130. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +7 -6
  131. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +41 -2
  132. rasa/dialogue_understanding/patterns/continue_interrupted.py +163 -1
  133. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +51 -7
  134. rasa/dialogue_understanding/stack/dialogue_stack.py +123 -2
  135. rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +57 -0
  136. rasa/dialogue_understanding/stack/utils.py +3 -2
  137. rasa/dialogue_understanding_test/du_test_runner.py +7 -2
  138. rasa/dialogue_understanding_test/du_test_schema.yml +3 -3
  139. rasa/e2e_test/e2e_test_runner.py +5 -0
  140. rasa/e2e_test/e2e_test_schema.yml +3 -3
  141. rasa/model_manager/model_api.py +1 -1
  142. rasa/model_manager/socket_bridge.py +8 -2
  143. rasa/server.py +10 -0
  144. rasa/shared/agents/__init__.py +0 -0
  145. rasa/shared/agents/utils.py +35 -0
  146. rasa/shared/constants.py +5 -0
  147. rasa/shared/core/constants.py +12 -1
  148. rasa/shared/core/domain.py +5 -5
  149. rasa/shared/core/events.py +319 -0
  150. rasa/shared/core/flows/flows_list.py +2 -2
  151. rasa/shared/core/flows/flows_yaml_schema.json +101 -186
  152. rasa/shared/core/flows/steps/call.py +51 -5
  153. rasa/shared/core/flows/validation.py +45 -7
  154. rasa/shared/core/flows/yaml_flows_io.py +3 -3
  155. rasa/shared/providers/llm/_base_litellm_client.py +39 -7
  156. rasa/shared/providers/llm/litellm_router_llm_client.py +8 -4
  157. rasa/shared/providers/llm/llm_client.py +7 -3
  158. rasa/shared/providers/llm/llm_response.py +49 -0
  159. rasa/shared/providers/llm/self_hosted_llm_client.py +8 -4
  160. rasa/shared/utils/common.py +2 -1
  161. rasa/shared/utils/llm.py +28 -5
  162. rasa/shared/utils/mcp/__init__.py +0 -0
  163. rasa/shared/utils/mcp/server_connection.py +157 -0
  164. rasa/shared/utils/schemas/events.py +42 -0
  165. rasa/studio/upload.py +4 -7
  166. rasa/tracing/instrumentation/instrumentation.py +4 -2
  167. rasa/utils/common.py +53 -0
  168. rasa/utils/licensing.py +21 -10
  169. rasa/utils/plotting.py +1 -1
  170. rasa/version.py +1 -1
  171. {rasa_pro-3.13.7.dist-info → rasa_pro-3.14.0.dev2.dist-info}/METADATA +16 -15
  172. {rasa_pro-3.13.7.dist-info → rasa_pro-3.14.0.dev2.dist-info}/RECORD +175 -138
  173. rasa/core/channels/inspector/dist/assets/channel-51d02e9e.js +0 -1
  174. rasa/core/channels/inspector/dist/assets/clone-cc738fa6.js +0 -1
  175. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-0c716443.js +0 -1
  176. rasa/core/channels/inspector/dist/assets/index-c804b295.js +0 -1335
  177. {rasa_pro-3.13.7.dist-info → rasa_pro-3.14.0.dev2.dist-info}/NOTICE +0 -0
  178. {rasa_pro-3.13.7.dist-info → rasa_pro-3.14.0.dev2.dist-info}/WHEEL +0 -0
  179. {rasa_pro-3.13.7.dist-info → rasa_pro-3.14.0.dev2.dist-info}/entry_points.txt +0 -0
@@ -1,14 +1,22 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Any, Dict, List, Optional, Text
3
+ import asyncio
4
+ from typing import Any, Dict, List, Optional, Text, cast
4
5
 
5
6
  import structlog
6
7
  from jinja2 import Template
7
8
  from pypred import Predicate
8
- from structlog.contextvars import (
9
- bound_contextvars,
10
- )
9
+ from structlog.contextvars import bound_contextvars
11
10
 
11
+ from rasa.agents.agent_manager import AgentManager
12
+ from rasa.agents.constants import (
13
+ AGENT_METADATA_AGENT_RESPONSE_KEY,
14
+ AGENT_METADATA_EXIT_IF_KEY,
15
+ AGENT_METADATA_TOOL_RESULTS_KEY,
16
+ )
17
+ from rasa.agents.core.types import AgentStatus, ProtocolType
18
+ from rasa.agents.schemas import AgentInput, AgentOutput
19
+ from rasa.core.available_agents import AvailableAgents
12
20
  from rasa.core.available_endpoints import AvailableEndpoints
13
21
  from rasa.core.constants import ACTIVE_FLOW_METADATA_KEY, STEP_ID_METADATA_KEY
14
22
  from rasa.core.policies.flows.flow_exceptions import (
@@ -22,7 +30,8 @@ from rasa.core.policies.flows.flow_step_result import (
22
30
  FlowStepResult,
23
31
  PauseFlowReturnPrediction,
24
32
  )
25
- from rasa.dialogue_understanding.commands import CancelFlowCommand
33
+ from rasa.core.policies.flows.mcp_tool_executor import call_mcp_tool
34
+ from rasa.core.utils import get_slot_names_from_exit_conditions
26
35
  from rasa.dialogue_understanding.patterns.cancel import CancelPatternFlowStackFrame
27
36
  from rasa.dialogue_understanding.patterns.collect_information import (
28
37
  FLOW_PATTERN_COLLECT_INFORMATION,
@@ -49,17 +58,27 @@ from rasa.dialogue_understanding.stack.frames import (
49
58
  UserFlowStackFrame,
50
59
  )
51
60
  from rasa.dialogue_understanding.stack.frames.flow_stack_frame import (
61
+ AgentStackFrame,
62
+ AgentState,
52
63
  FlowStackFrameType,
53
64
  )
54
- from rasa.dialogue_understanding.stack.utils import (
55
- top_user_flow_frame,
56
- )
65
+ from rasa.dialogue_understanding.stack.utils import top_user_flow_frame
66
+ from rasa.shared.agents.utils import get_protocol_type
57
67
  from rasa.shared.constants import RASA_PATTERN_HUMAN_HANDOFF
58
68
  from rasa.shared.core.constants import (
69
+ ACTION_AGENT_REQUEST_USER_INPUT_NAME,
59
70
  ACTION_LISTEN_NAME,
71
+ ACTION_METADATA_MESSAGE_KEY,
72
+ ACTION_METADATA_TEXT_KEY,
73
+ ACTION_SEND_TEXT_NAME,
74
+ FLOW_HASHES_SLOT,
60
75
  SILENCE_TIMEOUT_SLOT,
61
76
  )
62
77
  from rasa.shared.core.events import (
78
+ AgentCancelled,
79
+ AgentCompleted,
80
+ AgentResumed,
81
+ AgentStarted,
63
82
  Event,
64
83
  FlowCompleted,
65
84
  FlowResumed,
@@ -67,11 +86,7 @@ from rasa.shared.core.events import (
67
86
  SlotSet,
68
87
  )
69
88
  from rasa.shared.core.flows import FlowsList
70
- from rasa.shared.core.flows.flow import (
71
- END_STEP,
72
- Flow,
73
- FlowStep,
74
- )
89
+ from rasa.shared.core.flows.flow import END_STEP, Flow, FlowStep
75
90
  from rasa.shared.core.flows.flow_step_links import (
76
91
  ElseFlowStepLink,
77
92
  IfFlowStepLink,
@@ -89,14 +104,19 @@ from rasa.shared.core.flows.steps import (
89
104
  )
90
105
  from rasa.shared.core.flows.steps.constants import START_STEP
91
106
  from rasa.shared.core.slots import Slot, SlotRejection
92
- from rasa.shared.core.trackers import (
93
- DialogueStateTracker,
94
- )
107
+ from rasa.shared.core.trackers import DialogueStateTracker
108
+ from rasa.shared.utils.llm import tracker_as_readable_transcript
95
109
 
96
110
  structlogger = structlog.get_logger()
97
111
 
98
112
  MAX_NUMBER_OF_STEPS = 250
99
113
 
114
+ MAX_AGENT_RETRY_DELAY_SECONDS = 5
115
+ MAX_AGENT_RETRIES = 3
116
+
117
+ # Slots that should not be forwarded to sub-agents via AgentInput
118
+ SLOTS_EXCLUDED_FOR_AGENT = [FLOW_HASHES_SLOT]
119
+
100
120
 
101
121
  def render_template_variables(text: str, context: Dict[Text, Any]) -> str:
102
122
  """Replace context variables in a text."""
@@ -148,6 +168,13 @@ def select_next_step_id(
148
168
  tracker: DialogueStateTracker,
149
169
  ) -> Optional[Text]:
150
170
  """Selects the next step id based on the current step."""
171
+ # if the current step is a call step to an agent, and we already have an
172
+ # AgentStackFrame on top of the stack, we need to return the current
173
+ # step id again in order to loop back to the agent.
174
+ agent_stack_frame = tracker.stack.top()
175
+ if agent_stack_frame and isinstance(agent_stack_frame, AgentStackFrame):
176
+ return current.id
177
+
151
178
  next_step = current.next
152
179
  if len(next_step.links) == 1 and isinstance(next_step.links[0], StaticFlowStepLink):
153
180
  return next_step.links[0].target
@@ -359,7 +386,7 @@ def reset_scoped_slots(
359
386
  return events
360
387
 
361
388
 
362
- def advance_flows(
389
+ async def advance_flows(
363
390
  tracker: DialogueStateTracker, available_actions: List[str], flows: FlowsList
364
391
  ) -> FlowActionPrediction:
365
392
  """Advance the current flows until the next action.
@@ -377,10 +404,10 @@ def advance_flows(
377
404
  # if there are no flows, there is nothing to do
378
405
  return FlowActionPrediction(None, 0.0)
379
406
 
380
- return advance_flows_until_next_action(tracker, available_actions, flows)
407
+ return await advance_flows_until_next_action(tracker, available_actions, flows)
381
408
 
382
409
 
383
- def advance_flows_until_next_action(
410
+ async def advance_flows_until_next_action(
384
411
  tracker: DialogueStateTracker,
385
412
  available_actions: List[str],
386
413
  flows: FlowsList,
@@ -441,7 +468,7 @@ def advance_flows_until_next_action(
441
468
 
442
469
  with bound_contextvars(step_id=next_step.id):
443
470
  step_stack = tracker.stack
444
- step_result = run_step(
471
+ step_result = await run_step(
445
472
  next_step,
446
473
  current_flow,
447
474
  step_stack,
@@ -477,10 +504,9 @@ def advance_flows_until_next_action(
477
504
  # make sure we really return all events that got created during the
478
505
  # step execution of all steps (not only the last one)
479
506
  prediction.events = gathered_events
480
- prediction.metadata = {
481
- ACTIVE_FLOW_METADATA_KEY: tracker.active_flow,
482
- STEP_ID_METADATA_KEY: tracker.current_step_id,
483
- }
507
+ prediction.metadata = prediction.metadata or {}
508
+ prediction.metadata[ACTIVE_FLOW_METADATA_KEY] = tracker.active_flow
509
+ prediction.metadata[STEP_ID_METADATA_KEY] = tracker.current_step_id
484
510
  return prediction
485
511
  else:
486
512
  structlogger.warning("flow.step.execution.no_action")
@@ -522,6 +548,8 @@ def validate_collect_step(
522
548
 
523
549
  def cancel_flow_and_push_internal_error(stack: DialogueStack, flow_name: str) -> None:
524
550
  """Cancel the top user flow and push the internal error pattern."""
551
+ from rasa.dialogue_understanding.commands import CancelFlowCommand
552
+
525
553
  top_frame = stack.top()
526
554
 
527
555
  if isinstance(top_frame, BaseFlowStackFrame):
@@ -550,7 +578,7 @@ def attach_stack_metadata_to_events(
550
578
  event.metadata[ACTIVE_FLOW_METADATA_KEY] = flow_id
551
579
 
552
580
 
553
- def run_step(
581
+ async def run_step(
554
582
  step: FlowStep,
555
583
  flow: Flow,
556
584
  stack: DialogueStack,
@@ -615,7 +643,7 @@ def run_step(
615
643
  return _run_link_step(initial_events, stack, step)
616
644
 
617
645
  elif isinstance(step, CallFlowStep):
618
- return _run_call_step(initial_events, stack, step)
646
+ return await _run_call_step(initial_events, stack, step, tracker)
619
647
 
620
648
  elif isinstance(step, SetSlotsFlowStep):
621
649
  return _run_set_slot_step(initial_events, step)
@@ -684,17 +712,25 @@ def _run_set_slot_step(
684
712
  return ContinueFlowWithNextStep(events=initial_events + slot_events)
685
713
 
686
714
 
687
- def _run_call_step(
688
- initial_events: List[Event], stack: DialogueStack, step: CallFlowStep
715
+ async def _run_call_step(
716
+ initial_events: List[Event],
717
+ stack: DialogueStack,
718
+ step: CallFlowStep,
719
+ tracker: DialogueStateTracker,
689
720
  ) -> FlowStepResult:
690
721
  structlogger.debug("flow.step.run.call")
691
- stack.push(
692
- UserFlowStackFrame(
693
- flow_id=step.call,
694
- frame_type=FlowStackFrameType.CALL,
695
- ),
696
- )
697
- return ContinueFlowWithNextStep(events=initial_events)
722
+ if step.is_calling_mcp_tool():
723
+ return await call_mcp_tool(initial_events, stack, step, tracker)
724
+ elif step.is_calling_agent():
725
+ return await run_agent(initial_events, stack, step, tracker)
726
+ else:
727
+ stack.push(
728
+ UserFlowStackFrame(
729
+ flow_id=step.call,
730
+ frame_type=FlowStackFrameType.CALL,
731
+ ),
732
+ )
733
+ return ContinueFlowWithNextStep(events=initial_events)
698
734
 
699
735
 
700
736
  def _run_link_step(
@@ -832,3 +868,306 @@ def _append_global_silence_timeout_event(
832
868
  AvailableEndpoints.get_instance().interaction_handling.global_silence_timeout,
833
869
  )
834
870
  )
871
+
872
+
873
+ def _reset_slots_covered_by_exit_if(
874
+ exit_conditions: List[str], tracker: DialogueStateTracker
875
+ ) -> None:
876
+ """Reset the slots covered by the exit_if condition."""
877
+ reset_slot_names = get_slot_names_from_exit_conditions(exit_conditions)
878
+ for slot_name in reset_slot_names:
879
+ if tracker.slots.get(slot_name) is not None:
880
+ tracker.update(SlotSet(slot_name, None))
881
+
882
+
883
+ async def run_agent(
884
+ initial_events: List[Event],
885
+ stack: DialogueStack,
886
+ step: CallFlowStep,
887
+ tracker: DialogueStateTracker,
888
+ ) -> FlowStepResult:
889
+ """Run an agent call step."""
890
+ structlogger.debug(
891
+ "flow.step.run_agent", agent_id=step.call, step_id=step.id, flow_id=step.flow_id
892
+ )
893
+
894
+ final_events = initial_events
895
+ agent_stack_frame = tracker.stack.find_agent_stack_frame_by_agent(
896
+ agent_id=step.call
897
+ )
898
+
899
+ if (
900
+ agent_stack_frame
901
+ and agent_stack_frame == stack.top()
902
+ and agent_stack_frame.state == AgentState.INTERRUPTED
903
+ ):
904
+ structlogger.debug(
905
+ "flow.step.run_agent.resume_interrupted_agent",
906
+ agent_id=step.call,
907
+ step_id=step.id,
908
+ flow_id=step.flow_id,
909
+ )
910
+ # The agent was previously interrupted when waiting for user input.
911
+ # Now we're back to the agent execution step and need to output the last message
912
+ # from the agent (user input request) again and wait for user input
913
+ cast(AgentStackFrame, stack.top()).state = AgentState.WAITING_FOR_INPUT
914
+ tracker.update_stack(stack)
915
+ utterance = (
916
+ agent_stack_frame.metadata.get(AGENT_METADATA_AGENT_RESPONSE_KEY, "")
917
+ if agent_stack_frame.metadata
918
+ else ""
919
+ )
920
+ final_events.append(AgentResumed(agent_id=step.call, flow_id=step.flow_id))
921
+ return PauseFlowReturnPrediction(
922
+ _create_agent_request_user_input_prediction(utterance, final_events)
923
+ )
924
+
925
+ agent_input_metadata = (
926
+ agent_stack_frame.metadata
927
+ if agent_stack_frame and agent_stack_frame.metadata
928
+ else {}
929
+ )
930
+ if step.exit_if:
931
+ # TODO: this is a temporary fix to reset the slots covered by the exit_if
932
+ if (
933
+ agent_stack_frame
934
+ and agent_stack_frame.frame_id == f"restart_agent_{step.call}"
935
+ ):
936
+ # when restarting an agent, we need to reset the slots covered by the
937
+ # exit_if condition so that the agent can run again.
938
+ _reset_slots_covered_by_exit_if(step.exit_if, tracker)
939
+ agent_input_metadata[AGENT_METADATA_EXIT_IF_KEY] = step.exit_if
940
+ agent_input = AgentInput(
941
+ id=step.call,
942
+ user_message=tracker.latest_message.text or ""
943
+ if tracker.latest_message
944
+ else "",
945
+ slots=_filter_slots_for_agent(tracker.current_slot_values()),
946
+ conversation_history=tracker_as_readable_transcript(tracker),
947
+ events=tracker.current_state().get("events") or [],
948
+ metadata=agent_input_metadata,
949
+ )
950
+
951
+ final_events.append(AgentStarted(step.call, step.flow_id))
952
+
953
+ protocol_type = get_protocol_type(step, AvailableAgents.get_agent_config(step.call))
954
+ # send the input to the agent and wait for a response
955
+ structlogger.debug(
956
+ "flow.step.run_agent.agent_input",
957
+ agent_name=step.call,
958
+ step_id=step.id,
959
+ flow_id=step.flow_id,
960
+ agent_input=agent_input,
961
+ )
962
+ output: AgentOutput = await _call_agent_with_retry(
963
+ agent_name=step.call,
964
+ protocol_type=protocol_type,
965
+ agent_input=agent_input,
966
+ max_retries=MAX_AGENT_RETRIES,
967
+ )
968
+ structlogger.debug(
969
+ "flow.step.run_agent.agent_response",
970
+ agent_name=step.call,
971
+ step_id=step.id,
972
+ flow_id=step.flow_id,
973
+ agent_response=output,
974
+ )
975
+
976
+ # add the set slot events returned by the agent to the list of final events
977
+ if output.events:
978
+ final_events.extend(output.events)
979
+
980
+ if output.status == AgentStatus.INPUT_REQUIRED:
981
+ output.metadata = output.metadata or {}
982
+ output.metadata[AGENT_METADATA_AGENT_RESPONSE_KEY] = (
983
+ output.response_message or ""
984
+ )
985
+ output.metadata[AGENT_METADATA_TOOL_RESULTS_KEY] = output.tool_results or []
986
+
987
+ top_stack_frame = stack.top()
988
+ # update the agent stack frame if it is already on the stack
989
+ # otherwise push a new one
990
+ if isinstance(top_stack_frame, AgentStackFrame):
991
+ top_stack_frame.state = AgentState.WAITING_FOR_INPUT
992
+ top_stack_frame.metadata = output.metadata
993
+ top_stack_frame.step_id = step.id
994
+ top_stack_frame.agent_id = step.call
995
+ top_stack_frame.flow_id = step.flow_id
996
+ else:
997
+ stack.push(
998
+ AgentStackFrame(
999
+ flow_id=step.flow_id,
1000
+ agent_id=step.call,
1001
+ state=AgentState.WAITING_FOR_INPUT,
1002
+ step_id=step.id,
1003
+ metadata=output.metadata,
1004
+ )
1005
+ )
1006
+
1007
+ action_prediction = _create_agent_request_user_input_prediction(
1008
+ output.response_message, final_events
1009
+ )
1010
+ return PauseFlowReturnPrediction(action_prediction)
1011
+ elif output.status == AgentStatus.COMPLETED:
1012
+ structlogger.debug(
1013
+ "flow.step.run_agent.completed",
1014
+ agent_name=step.call,
1015
+ step_id=step.id,
1016
+ flow_id=step.flow_id,
1017
+ )
1018
+ remove_agent_stack_frame(stack, step.call)
1019
+ agent_completed_event = AgentCompleted(agent_id=step.call, flow_id=step.flow_id)
1020
+ final_events.append(agent_completed_event)
1021
+ if output.response_message:
1022
+ # for open-ended agents we want to utter the last agent message
1023
+
1024
+ return PauseFlowReturnPrediction(
1025
+ _create_send_text_prediction(output.response_message, final_events)
1026
+ )
1027
+ else:
1028
+ return ContinueFlowWithNextStep(events=final_events)
1029
+ elif output.status == AgentStatus.FATAL_ERROR:
1030
+ # the agent failed, trigger pattern_internal_error
1031
+ structlogger.error(
1032
+ "flow.step.run_agent.fatal_error",
1033
+ agent_name=step.call,
1034
+ step_id=step.id,
1035
+ flow_id=step.flow_id,
1036
+ error_message=output.error_message,
1037
+ )
1038
+ remove_agent_stack_frame(stack, step.call)
1039
+ final_events.append(
1040
+ AgentCancelled(
1041
+ agent_id=step.call, flow_id=step.flow_id, reason=output.error_message
1042
+ )
1043
+ )
1044
+ stack.push(InternalErrorPatternFlowStackFrame())
1045
+ return ContinueFlowWithNextStep(events=final_events)
1046
+ else:
1047
+ structlogger.error(
1048
+ "flow.step.run_agent.unknown_status",
1049
+ agent_name=step.call,
1050
+ step_id=step.id,
1051
+ flow_id=step.flow_id,
1052
+ status=output.status,
1053
+ )
1054
+ remove_agent_stack_frame(stack, step.call)
1055
+ final_events.append(AgentCancelled(agent_id=step.call, flow_id=step.flow_id))
1056
+ stack.push(InternalErrorPatternFlowStackFrame())
1057
+ return ContinueFlowWithNextStep(events=final_events)
1058
+
1059
+
1060
+ def remove_agent_stack_frame(stack: DialogueStack, agent_id: str) -> None:
1061
+ """Finishes the agentic loop by popping the agent stack frame from the
1062
+ provided `stack`. The `tracker.stack` is NOT modified.
1063
+ """
1064
+ agent_stack_frame = stack.find_agent_stack_frame_by_agent(agent_id)
1065
+ if not agent_stack_frame:
1066
+ return
1067
+
1068
+ while removed_frame := stack.pop():
1069
+ structlogger.debug(
1070
+ "flow_executor.remove_agent_stack_frame",
1071
+ removed_frame=removed_frame,
1072
+ )
1073
+ if removed_frame == agent_stack_frame:
1074
+ break
1075
+
1076
+
1077
+ def _create_action_prediction(
1078
+ action_name: str, message: Optional[str], events: Optional[List[Event]]
1079
+ ) -> FlowActionPrediction:
1080
+ """Create a prediction for an action with a text message."""
1081
+ action_metadata = {
1082
+ ACTION_METADATA_MESSAGE_KEY: {
1083
+ ACTION_METADATA_TEXT_KEY: message,
1084
+ }
1085
+ }
1086
+ return FlowActionPrediction(
1087
+ action_name,
1088
+ 1.0,
1089
+ events=events if events else [],
1090
+ metadata=action_metadata,
1091
+ )
1092
+
1093
+
1094
+ def _create_agent_request_user_input_prediction(
1095
+ message: Optional[str], events: Optional[List[Event]]
1096
+ ) -> FlowActionPrediction:
1097
+ """Create a prediction for requesting user input from the agent
1098
+ and waiting for it.
1099
+ """
1100
+ return _create_action_prediction(
1101
+ ACTION_AGENT_REQUEST_USER_INPUT_NAME, message, events
1102
+ )
1103
+
1104
+
1105
+ def _create_send_text_prediction(
1106
+ message: Optional[str], events: Optional[List[Event]]
1107
+ ) -> FlowActionPrediction:
1108
+ """Create a prediction for sending a text message to the user."""
1109
+ return _create_action_prediction(ACTION_SEND_TEXT_NAME, message, events)
1110
+
1111
+
1112
+ async def _call_agent_with_retry(
1113
+ agent_name: str,
1114
+ protocol_type: ProtocolType,
1115
+ agent_input: AgentInput,
1116
+ max_retries: int,
1117
+ ) -> AgentOutput:
1118
+ """Call an agent with retries in case of recoverable errors."""
1119
+ for attempt in range(max_retries):
1120
+ if attempt > 0:
1121
+ structlogger.debug(
1122
+ "flow_executor.call_agent_with_retry.retrying",
1123
+ agent_name=agent_name,
1124
+ attempt=attempt + 1,
1125
+ num_retries=max_retries,
1126
+ )
1127
+
1128
+ agent_response: AgentOutput = await AgentManager().run_agent(
1129
+ agent_name=agent_name, protocol_type=protocol_type, context=agent_input
1130
+ )
1131
+ if agent_response.status != AgentStatus.RECOVERABLE_ERROR:
1132
+ return agent_response
1133
+
1134
+ structlogger.warning(
1135
+ "flow_executor.call_agent_with_retry.recoverable_error",
1136
+ agent_name=agent_name,
1137
+ attempt=attempt + 1,
1138
+ num_retries=max_retries,
1139
+ error_message=agent_response.error_message,
1140
+ )
1141
+ if attempt < max_retries - 1:
1142
+ # exponential backoff - wait longer with each retry
1143
+ # 1 second, 2 seconds, 4 seconds, etc.
1144
+ await asyncio.sleep(min(2**attempt, MAX_AGENT_RETRY_DELAY_SECONDS))
1145
+
1146
+ # we exhausted all retries, return fatal error
1147
+ structlogger.warning(
1148
+ "flow_executor.call_agent_with_retry.exhausted_retries",
1149
+ agent_name=agent_name,
1150
+ num_retries=max_retries,
1151
+ )
1152
+ return AgentOutput(
1153
+ id=agent_name,
1154
+ status=AgentStatus.FATAL_ERROR,
1155
+ error_message="Exhausted all retries for agent call.",
1156
+ )
1157
+
1158
+
1159
+ def _filter_slots_for_agent(slots: Dict[str, Any]) -> Dict[str, Any]:
1160
+ """Filter out slots that should not be forwarded to agents.
1161
+
1162
+ Args:
1163
+ slots: The full slot dictionary from the tracker.
1164
+
1165
+ Returns:
1166
+ A copy of the slot dictionary excluding slots listed in
1167
+ `SLOTS_EXCLUDED_FOR_AGENT`.
1168
+ """
1169
+ return {
1170
+ key: value
1171
+ for key, value in slots.items()
1172
+ if key not in SLOTS_EXCLUDED_FOR_AGENT
1173
+ }