rasa-pro 3.13.6__py3-none-any.whl → 3.14.0.dev1__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 (178) 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/continue_agent_command.py +91 -0
  118. rasa/dialogue_understanding/commands/knowledge_answer_command.py +11 -0
  119. rasa/dialogue_understanding/commands/restart_agent_command.py +146 -0
  120. rasa/dialogue_understanding/commands/start_flow_command.py +129 -8
  121. rasa/dialogue_understanding/commands/utils.py +6 -2
  122. rasa/dialogue_understanding/generator/command_parser.py +4 -0
  123. rasa/dialogue_understanding/generator/llm_based_command_generator.py +50 -12
  124. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +61 -0
  125. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +61 -0
  126. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +81 -0
  127. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +81 -0
  128. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +7 -6
  129. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +7 -6
  130. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +41 -2
  131. rasa/dialogue_understanding/patterns/continue_interrupted.py +163 -1
  132. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +51 -7
  133. rasa/dialogue_understanding/processor/command_processor.py +27 -11
  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/tracing/instrumentation/instrumentation.py +4 -2
  166. rasa/utils/common.py +53 -0
  167. rasa/utils/licensing.py +21 -10
  168. rasa/utils/plotting.py +1 -1
  169. rasa/version.py +1 -1
  170. {rasa_pro-3.13.6.dist-info → rasa_pro-3.14.0.dev1.dist-info}/METADATA +16 -15
  171. {rasa_pro-3.13.6.dist-info → rasa_pro-3.14.0.dev1.dist-info}/RECORD +174 -137
  172. rasa/core/channels/inspector/dist/assets/channel-51d02e9e.js +0 -1
  173. rasa/core/channels/inspector/dist/assets/clone-cc738fa6.js +0 -1
  174. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-0c716443.js +0 -1
  175. rasa/core/channels/inspector/dist/assets/index-c804b295.js +0 -1335
  176. {rasa_pro-3.13.6.dist-info → rasa_pro-3.14.0.dev1.dist-info}/NOTICE +0 -0
  177. {rasa_pro-3.13.6.dist-info → rasa_pro-3.14.0.dev1.dist-info}/WHEEL +0 -0
  178. {rasa_pro-3.13.6.dist-info → rasa_pro-3.14.0.dev1.dist-info}/entry_points.txt +0 -0
@@ -8,6 +8,13 @@ from typing import Any, Callable, Dict, List, Optional
8
8
  import jsonpatch
9
9
  import structlog
10
10
 
11
+ from rasa.dialogue_understanding.stack.frames.flow_stack_frame import (
12
+ AgentStackFrame,
13
+ AgentState,
14
+ FlowStackFrameType,
15
+ UserFlowStackFrame,
16
+ )
17
+
11
18
  if typing.TYPE_CHECKING:
12
19
  from rasa.dialogue_understanding.stack.frames import DialogueStackFrame
13
20
 
@@ -91,6 +98,42 @@ class DialogueStack:
91
98
  """
92
99
  return self.frames.pop()
93
100
 
101
+ def move_frames_to_top(self, frames_to_move: List["DialogueStackFrame"]) -> None:
102
+ """Moves specified frames to top of stack while preserving their relative order.
103
+
104
+ Args:
105
+ frames_to_move: The frames to move to the top of the stack.
106
+ """
107
+ # Get frames that are not being moved
108
+ frames_to_keep = [frame for frame in self.frames if frame not in frames_to_move]
109
+
110
+ # Reorder: keep frames first, then moved frames
111
+ self.frames = frames_to_keep + frames_to_move
112
+
113
+ # set all frames to interrupt except for LINK and CALL
114
+ for frame in self.frames:
115
+ if (
116
+ isinstance(frame, UserFlowStackFrame)
117
+ and frame.frame_type == FlowStackFrameType.LINK
118
+ ):
119
+ continue
120
+ if (
121
+ isinstance(frame, UserFlowStackFrame)
122
+ and frame.frame_type == FlowStackFrameType.CALL
123
+ ):
124
+ continue
125
+ if (
126
+ isinstance(frame, UserFlowStackFrame)
127
+ and frame.frame_type == FlowStackFrameType.REGULAR
128
+ ):
129
+ frame.frame_type = FlowStackFrameType.INTERRUPT
130
+
131
+ # set the first frame to regular
132
+ for frame in self.frames:
133
+ if isinstance(frame, UserFlowStackFrame):
134
+ frame.frame_type = FlowStackFrameType.REGULAR
135
+ return
136
+
94
137
  def current_context(self) -> Dict[str, Any]:
95
138
  """Returns the context of the topmost frame.
96
139
 
@@ -109,7 +152,7 @@ class DialogueStack:
109
152
  """Returns the topmost frame from the stack.
110
153
 
111
154
  Args:
112
- ignore_frame: The ID of the flow to ignore. Picks the top most
155
+ ignore: The ID of the flow to ignore. Picks the top most
113
156
  frame that has a different flow ID.
114
157
 
115
158
  Returns:
@@ -136,7 +179,8 @@ class DialogueStack:
136
179
  patch_dump: The patch to apply to the stack.
137
180
 
138
181
  Returns:
139
- The updated stack."""
182
+ The updated stack.
183
+ """
140
184
  patch = jsonpatch.JsonPatch.from_string(patch_dump)
141
185
  dialogue_stack_dump = patch.apply(self.as_dict())
142
186
  return DialogueStack.from_dict(dialogue_stack_dump)
@@ -177,3 +221,80 @@ class DialogueStack:
177
221
  if patch:
178
222
  return patch.to_string()
179
223
  return None
224
+
225
+ def _find_agent_frame_by_predicate(
226
+ self, predicate: Callable[[AgentStackFrame], bool]
227
+ ) -> List[AgentStackFrame]:
228
+ stack_frames: List[AgentStackFrame] = []
229
+ for stack_frame in reversed(self.frames):
230
+ if isinstance(stack_frame, AgentStackFrame) and predicate(stack_frame):
231
+ stack_frames.append(stack_frame)
232
+ return stack_frames
233
+
234
+ def find_active_agent_frame(self) -> Optional[AgentStackFrame]:
235
+ stack_frames = self._find_agent_frame_by_predicate(
236
+ lambda frame: frame.state == AgentState.WAITING_FOR_INPUT
237
+ )
238
+ if stack_frames:
239
+ return stack_frames[0]
240
+ return None
241
+
242
+ def find_agent_stack_frame_by_agent(
243
+ self, agent_id: str
244
+ ) -> Optional[AgentStackFrame]:
245
+ """Get the agent stack frame for a specific agent ID.
246
+
247
+ May also include the agent stack frame in the INTERRUPTED state.
248
+ """
249
+ stack_frames = self._find_agent_frame_by_predicate(
250
+ lambda frame: frame.agent_id == agent_id
251
+ )
252
+ if stack_frames:
253
+ return stack_frames[0]
254
+ return None
255
+
256
+ def find_active_agent_stack_frame_for_flow(
257
+ self, flow_id: str
258
+ ) -> Optional[AgentStackFrame]:
259
+ """Get the agent stack frame of a specific flow."""
260
+ stack_frames = self._find_agent_frame_by_predicate(
261
+ lambda frame: frame.flow_id == flow_id
262
+ )
263
+ for stack_frame in stack_frames:
264
+ if stack_frame.state == AgentState.WAITING_FOR_INPUT:
265
+ return stack_frame
266
+ return None
267
+
268
+ def get_active_agent_id(self) -> Optional[typing.Text]:
269
+ agent_frame = self.find_active_agent_frame()
270
+ if agent_frame:
271
+ return agent_frame.agent_id
272
+ return None
273
+
274
+ def agent_is_active(self) -> bool:
275
+ return self.find_active_agent_frame() is not None
276
+
277
+ def get_all_user_flow_frames(
278
+ self, ignore_call_and_link_frames: bool = True
279
+ ) -> List[UserFlowStackFrame]:
280
+ """Get all user flow frames from the dialogue stack.
281
+
282
+ Args:
283
+ ignore_call_and_link_frames: Whether to ignore user frames of type `call`
284
+ and `link`. By default, these frames are ignored.
285
+
286
+ Returns:
287
+ A list of all user flow frames in the dialogue stack.
288
+ """
289
+ return [
290
+ frame
291
+ for frame in self.frames
292
+ if isinstance(frame, UserFlowStackFrame)
293
+ and (
294
+ not ignore_call_and_link_frames
295
+ or (
296
+ frame.frame_type != FlowStackFrameType.CALL
297
+ and frame.frame_type != FlowStackFrameType.LINK
298
+ )
299
+ )
300
+ ]
@@ -94,6 +94,18 @@ class InvalidFlowStepIdException(Exception):
94
94
  super().__init__(f"Invalid flow step ID '{step_id}' for flow '{flow_id}'.")
95
95
 
96
96
 
97
+ class InvalidAgentState(RasaException):
98
+ """Raised if the agent state is invalid."""
99
+
100
+ def __init__(self, invalid_state: str) -> None:
101
+ """Creates a `InvalidAgentState`.
102
+
103
+ Args:
104
+ invalid_state: The invalid agent state.
105
+ """
106
+ super().__init__(f"Invalid agent state '{invalid_state}'.")
107
+
108
+
97
109
  @dataclass
98
110
  class BaseFlowStackFrame(DialogueStackFrame):
99
111
  flow_id: str = "" # needed to avoid "default arg before non-default" error
@@ -171,3 +183,48 @@ class UserFlowStackFrame(BaseFlowStackFrame):
171
183
  step_id=data["step_id"],
172
184
  frame_type=FlowStackFrameType.from_str(data.get("frame_type")),
173
185
  )
186
+
187
+
188
+ class AgentState(str, Enum):
189
+ INTERRUPTED = "interrupted"
190
+ WAITING_FOR_INPUT = "waiting_for_input"
191
+
192
+ @staticmethod
193
+ def from_str(state: Optional[str]) -> AgentState:
194
+ if state == AgentState.WAITING_FOR_INPUT.value:
195
+ return AgentState.WAITING_FOR_INPUT
196
+ elif state == AgentState.INTERRUPTED.value:
197
+ return AgentState.INTERRUPTED
198
+ else:
199
+ raise InvalidAgentState(state)
200
+
201
+
202
+ @dataclass
203
+ class AgentStackFrame(BaseFlowStackFrame):
204
+ agent_id: str = ""
205
+ state: AgentState = AgentState.WAITING_FOR_INPUT
206
+ metadata: Optional[Dict[str, Any]] = None
207
+
208
+ @classmethod
209
+ def type(cls) -> str:
210
+ """Returns the type of the frame."""
211
+ return "agent"
212
+
213
+ @staticmethod
214
+ def from_dict(data: Dict[str, Any]) -> AgentStackFrame:
215
+ """Creates a `AgentStackFrame` from a dictionary.
216
+
217
+ Args:
218
+ data: The dictionary to create the `AgentStackFrame` from.
219
+
220
+ Returns:
221
+ The created `AgentStackFrame`.
222
+ """
223
+ return AgentStackFrame(
224
+ frame_id=data["frame_id"],
225
+ flow_id=data["flow_id"],
226
+ step_id=data["step_id"],
227
+ agent_id=data["agent_id"],
228
+ state=AgentState.from_str(data["state"]),
229
+ metadata=data.get("metadata"),
230
+ )
@@ -198,8 +198,9 @@ def end_top_user_flow(stack: DialogueStack) -> DialogueStack:
198
198
  def get_collect_steps_excluding_ask_before_filling_for_active_flow(
199
199
  dialogue_stack: DialogueStack, all_flows: FlowsList
200
200
  ) -> Set[str]:
201
- """Get all collect steps that are part of the current flow, without
202
- considering the collect steps that has to be asked before filling.
201
+ """Get all collect steps that are part of the current flow.
202
+
203
+ Collect steps that have to be asked before filling are not considered.
203
204
 
204
205
  Args:
205
206
  dialogue_stack: The dialogue stack.
@@ -1,10 +1,12 @@
1
1
  import asyncio
2
2
  import time
3
- from typing import Any, Dict, List, Optional, Text
3
+ from pathlib import Path
4
+ from typing import Any, Dict, List, Optional, Union
4
5
 
5
6
  import structlog
6
7
  from tqdm import tqdm
7
8
 
9
+ from rasa.core.available_agents import AvailableAgents
8
10
  from rasa.core.available_endpoints import AvailableEndpoints
9
11
  from rasa.core.channels import CollectingOutputChannel, UserMessage
10
12
  from rasa.core.exceptions import AgentNotReady
@@ -52,10 +54,11 @@ class DialogueUnderstandingTestRunner:
52
54
 
53
55
  def __init__(
54
56
  self,
55
- model_path: Optional[Text] = None,
57
+ model_path: Optional[str] = None,
56
58
  model_server: Optional[EndpointConfig] = None,
57
59
  remote_storage: Optional[StorageType] = None,
58
60
  endpoints: Optional[AvailableEndpoints] = None,
61
+ sub_agents_path: Optional[Union[Path, str]] = None,
59
62
  ) -> None:
60
63
  """Initializes the Dialogue Understanding test suite runner.
61
64
 
@@ -68,6 +71,7 @@ class DialogueUnderstandingTestRunner:
68
71
  import rasa.core.agent
69
72
 
70
73
  self._check_action_server(endpoints)
74
+ sub_agents = AvailableAgents.get_instance(sub_agents_path)
71
75
 
72
76
  self.agent = asyncio.run(
73
77
  rasa.core.agent.load_agent(
@@ -75,6 +79,7 @@ class DialogueUnderstandingTestRunner:
75
79
  model_server=model_server,
76
80
  remote_storage=remote_storage,
77
81
  endpoints=endpoints,
82
+ sub_agents=sub_agents,
78
83
  )
79
84
  )
80
85
  if not self.agent.is_ready():
@@ -5,12 +5,12 @@ mapping:
5
5
  sequence:
6
6
  - type: map
7
7
  mapping:
8
- regex;(^[a-zA-Z_]+[a-zA-Z0-9_]*$):
8
+ regex;(^[a-zA-Z_]+[a-zA-Z0-9_\-]*$):
9
9
  type: "seq"
10
10
  sequence:
11
11
  - type: map
12
12
  mapping:
13
- regex;(^[a-zA-Z_]+[a-zA-Z0-9_]*$):
13
+ regex;(^[a-zA-Z_]+[a-zA-Z0-9_\-]*$):
14
14
  type: any
15
15
 
16
16
  metadata:
@@ -129,7 +129,7 @@ mapping:
129
129
  type: "seq"
130
130
  sequence:
131
131
  - type: "str"
132
- pattern: ^[a-zA-Z_]+[a-zA-Z0-9_]*$
132
+ pattern: ^[a-zA-Z_]+[a-zA-Z0-9_\-]*$
133
133
  metadata:
134
134
  type: "str"
135
135
  pattern: ^[a-zA-Z_]+[a-zA-Z0-9_]*$
@@ -13,6 +13,7 @@ import structlog
13
13
  from tqdm import tqdm
14
14
 
15
15
  import rasa.shared.utils.io
16
+ from rasa.core.available_agents import AvailableAgents
16
17
  from rasa.core.available_endpoints import AvailableEndpoints
17
18
  from rasa.core.channels import CollectingOutputChannel, UserMessage
18
19
  from rasa.core.constants import ACTIVE_FLOW_METADATA_KEY, STEP_ID_METADATA_KEY
@@ -65,6 +66,7 @@ class E2ETestRunner:
65
66
  model_server: Optional[EndpointConfig] = None,
66
67
  remote_storage: Optional[StorageType] = None,
67
68
  endpoints: Optional[AvailableEndpoints] = None,
69
+ sub_agents_path: Optional[Text] = None,
68
70
  **kwargs: Any,
69
71
  ) -> None:
70
72
  """Initializes the E2E test suite runner.
@@ -94,12 +96,15 @@ class E2ETestRunner:
94
96
  if endpoints and not are_custom_actions_stubbed:
95
97
  self._action_server_is_reachable(endpoints)
96
98
 
99
+ sub_agents = AvailableAgents.get_instance(sub_agents_path)
100
+
97
101
  self.agent = asyncio.run(
98
102
  rasa.core.agent.load_agent(
99
103
  model_path=model_path,
100
104
  model_server=model_server,
101
105
  remote_storage=remote_storage,
102
106
  endpoints=endpoints,
107
+ sub_agents=sub_agents,
103
108
  )
104
109
  )
105
110
 
@@ -5,12 +5,12 @@ mapping:
5
5
  sequence:
6
6
  - type: map
7
7
  mapping:
8
- regex;(^[a-zA-Z_]+[a-zA-Z0-9_]*$):
8
+ regex;(^[a-zA-Z_]+[a-zA-Z0-9_\-]*$):
9
9
  type: "seq"
10
10
  sequence:
11
11
  - type: map
12
12
  mapping:
13
- regex;(^[a-zA-Z_]+[a-zA-Z0-9_]*$):
13
+ regex;(^[a-zA-Z_]+[a-zA-Z0-9_\-]*$):
14
14
  type: any
15
15
 
16
16
  metadata:
@@ -129,7 +129,7 @@ mapping:
129
129
  type: "seq"
130
130
  sequence:
131
131
  - type: "str"
132
- pattern: ^[a-zA-Z_]+[a-zA-Z0-9_]*$
132
+ pattern: ^[a-zA-Z_]+[a-zA-Z0-9_\-]*$
133
133
  metadata:
134
134
  type: "str"
135
135
  pattern: ^[a-zA-Z_]+[a-zA-Z0-9_]*$
@@ -571,7 +571,7 @@ def external_blueprint() -> Blueprint:
571
571
  """Create a blueprint for the model manager API."""
572
572
  from rasa.core.channels.socketio import SocketBlueprint
573
573
 
574
- sio = AsyncServer(async_mode="sanic", cors_allowed_origins=[])
574
+ sio = AsyncServer(async_mode="sanic", cors_allowed_origins="*")
575
575
  bp = SocketBlueprint(sio, "", "model_api_external")
576
576
 
577
577
  create_bridge_server(sio, running_bots)
@@ -2,8 +2,7 @@ import json
2
2
  from typing import Any, Dict, Optional
3
3
 
4
4
  import structlog
5
- from socketio import AsyncServer
6
- from socketio.asyncio_client import AsyncClient
5
+ from socketio import AsyncClient, AsyncServer # type: ignore[attr-defined]
7
6
  from socketio.exceptions import ConnectionRefusedError
8
7
 
9
8
  from rasa.model_manager.runner_service import BotSession
@@ -131,6 +130,13 @@ async def create_bridge_client(
131
130
  structlogger.debug("model_runner.bot_message", deployment_id=deployment_id)
132
131
  await sio.emit("bot_message", data, room=sid)
133
132
 
133
+ @client.event # type: ignore[misc]
134
+ async def error(data: Dict[str, Any]) -> None:
135
+ structlogger.debug(
136
+ "model_runner.bot_error", deployment_id=deployment_id, data=data
137
+ )
138
+ await sio.emit("error", data, room=sid)
139
+
134
140
  @client.event # type: ignore[misc]
135
141
  async def tracker(data: Dict[str, Any]) -> None:
136
142
  await sio.emit("tracker", json.loads(data), room=sid)
rasa/server.py CHANGED
@@ -44,6 +44,7 @@ import rasa.utils.endpoints
44
44
  import rasa.utils.io
45
45
  from rasa.constants import MINIMUM_COMPATIBLE_VERSION
46
46
  from rasa.core.agent import Agent
47
+ from rasa.core.available_agents import AvailableAgents
47
48
  from rasa.core.available_endpoints import AvailableEndpoints
48
49
  from rasa.core.channels.channel import (
49
50
  CollectingOutputChannel,
@@ -693,6 +694,7 @@ def create_app(
693
694
  jwt_private_key: Optional[Text] = None,
694
695
  jwt_method: Text = "HS256",
695
696
  endpoints: Optional[AvailableEndpoints] = None,
697
+ sub_agents: Optional[AvailableAgents] = None,
696
698
  is_inspector_enabled: bool = False,
697
699
  ) -> Sanic:
698
700
  """Class representing a Rasa HTTP server."""
@@ -728,6 +730,7 @@ def create_app(
728
730
  user_id="username",
729
731
  )
730
732
 
733
+ app.ctx.sub_agents = sub_agents
731
734
  app.ctx.agent = agent
732
735
  # Initialize shared object of type unsigned int for tracking
733
736
  # the number of active training processes
@@ -1463,6 +1466,13 @@ def create_app(
1463
1466
  flows = await processor.get_flows()
1464
1467
  return response.json(flows.as_json_list())
1465
1468
 
1469
+ @app.get("/sub-agents")
1470
+ @requires_auth(app, auth_token)
1471
+ async def get_sub_agents(request: Request) -> HTTPResponse:
1472
+ """Get all the sub-agents currently stored by the agent."""
1473
+ sub_agents = app.ctx.sub_agents
1474
+ return response.json(sub_agents.as_json_list())
1475
+
1466
1476
  @app.get("/domain")
1467
1477
  @requires_auth(app, auth_token)
1468
1478
  @ensure_loaded_agent(app)
File without changes
@@ -0,0 +1,35 @@
1
+ from typing import Optional
2
+
3
+ from rasa.agents.core.types import AgentIdentifier, ProtocolType
4
+ from rasa.core.available_agents import AgentConfig, ProtocolConfig
5
+ from rasa.shared.core.flows.steps import CallFlowStep
6
+
7
+
8
+ def make_agent_identifier(
9
+ agent_name: str, protocol_type: ProtocolType
10
+ ) -> AgentIdentifier:
11
+ """Make an agent identifier."""
12
+ return AgentIdentifier(agent_name, protocol_type)
13
+
14
+
15
+ def get_protocol_type(
16
+ step: CallFlowStep, agent_config: Optional[AgentConfig]
17
+ ) -> ProtocolType:
18
+ """Get the protocol type for an agent.
19
+
20
+ Args:
21
+ step: The step that is calling the agent.
22
+ agent_config: The agent configuration.
23
+
24
+ Returns:
25
+ The protocol type for the agent.
26
+ """
27
+ if step.exit_if:
28
+ protocol_type = ProtocolType.MCP_TASK
29
+ else:
30
+ protocol_type = (
31
+ ProtocolType.A2A
32
+ if agent_config and agent_config.agent.protocol == ProtocolConfig.A2A
33
+ else ProtocolType.MCP_OPEN
34
+ )
35
+ return protocol_type
rasa/shared/constants.py CHANGED
@@ -345,6 +345,8 @@ PAYLOAD = "payload"
345
345
  # Used for LLM command generation
346
346
  ROLE_USER = "user"
347
347
  ROLE_SYSTEM = "system"
348
+ ROLE_ASSISTANT = "assistant"
349
+ ROLE_TOOL = "tool"
348
350
 
349
351
  # Used for key values in ValidateSlotPatternFlowStackFrame
350
352
  REFILL_UTTER = "refill_utter"
@@ -359,3 +361,6 @@ FAQ_INPUT_DATA_QUESTION_LINE_PREFIX = "Q:"
359
361
  FAQ_INPUT_DATA_ANSWER_LINE_PREFIX = "A:"
360
362
  FAQ_DOCUMENT_ENTRY_SEPARATOR = "\n\n"
361
363
  FAQ_DOCUMENT_LINE_SEPARATOR = "\n"
364
+
365
+ # Constants for the MCP server
366
+ KEY_TOOL_CALLS = "tool_calls"
@@ -30,6 +30,7 @@ LOOP_NAME = "name"
30
30
  ACTION_LISTEN_NAME = "action_listen"
31
31
  ACTION_RESTART_NAME = "action_restart"
32
32
  ACTION_SEND_TEXT_NAME = "action_send_text"
33
+ ACTION_AGENT_REQUEST_USER_INPUT_NAME = "action_agent_request_user_input"
33
34
  ACTION_SESSION_START_NAME = "action_session_start"
34
35
  ACTION_DEFAULT_FALLBACK_NAME = "action_default_fallback"
35
36
  ACTION_DEACTIVATE_LOOP_NAME = "action_deactivate_loop"
@@ -52,10 +53,16 @@ ACTION_TRIGGER_CHITCHAT = "action_trigger_chitchat"
52
53
  ACTION_RESET_ROUTING = "action_reset_routing"
53
54
  ACTION_HANGUP = "action_hangup"
54
55
  ACTION_REPEAT_BOT_MESSAGES = "action_repeat_bot_messages"
56
+ ACTION_CONTINUE_INTERRUPTED_FLOW = "action_continue_interrupted_flow"
57
+ ACTION_SET_INTERRUPTED_FLOWS = "action_set_interrupted_flows"
58
+ ACTION_CANCEL_INTERRUPTED_FLOW = "action_cancel_interrupted_flow"
59
+ ACTION_ASK_INTERRUPTED_FLOW_TO_CONTINUE = "action_ask_interrupted_flow_to_continue"
55
60
 
56
61
  ACTION_METADATA_EXECUTION_SUCCESS = "execution_success"
57
62
  ACTION_METADATA_EXECUTION_ERROR_MESSAGE = "execution_error_message"
58
63
 
64
+ ACTION_METADATA_MESSAGE_KEY = "message"
65
+ ACTION_METADATA_TEXT_KEY = "text"
59
66
 
60
67
  DEFAULT_ACTION_NAMES = [
61
68
  ACTION_LISTEN_NAME,
@@ -70,6 +77,7 @@ DEFAULT_ACTION_NAMES = [
70
77
  ACTION_UNLIKELY_INTENT_NAME,
71
78
  ACTION_BACK_NAME,
72
79
  ACTION_SEND_TEXT_NAME,
80
+ ACTION_AGENT_REQUEST_USER_INPUT_NAME,
73
81
  RULE_SNIPPET_ACTION_NAME,
74
82
  ACTION_EXTRACT_SLOTS,
75
83
  ACTION_CANCEL_FLOW,
@@ -82,6 +90,10 @@ DEFAULT_ACTION_NAMES = [
82
90
  ACTION_RESET_ROUTING,
83
91
  ACTION_HANGUP,
84
92
  ACTION_REPEAT_BOT_MESSAGES,
93
+ ACTION_CONTINUE_INTERRUPTED_FLOW,
94
+ ACTION_SET_INTERRUPTED_FLOWS,
95
+ ACTION_CANCEL_INTERRUPTED_FLOW,
96
+ ACTION_ASK_INTERRUPTED_FLOW_TO_CONTINUE,
85
97
  ]
86
98
 
87
99
  ACTION_SHOULD_SEND_DOMAIN = "send_domain"
@@ -136,7 +148,6 @@ DEFAULT_SLOT_NAMES = {
136
148
  FLOW_HASHES_SLOT,
137
149
  }
138
150
 
139
-
140
151
  SLOT_MAPPINGS = "mappings"
141
152
  MAPPING_CONDITIONS = "conditions"
142
153
  KEY_MAPPING_TYPE = "type"
@@ -508,8 +508,8 @@ class Domain:
508
508
  return combined
509
509
 
510
510
  def partial_merge(self, other: Domain) -> Domain:
511
- """
512
- Returns a new Domain with intersection-based merging:
511
+ """Returns a new Domain with intersection-based merging.
512
+
513
513
  - For each domain section only overwrite items that already exist in self.
514
514
  - Brand-new items in `other` are ignored.
515
515
 
@@ -543,9 +543,9 @@ class Domain:
543
543
  return Domain.from_dict(updated_self)
544
544
 
545
545
  def difference(self, other: Domain) -> Domain:
546
- """
547
- Returns a new Domain containing items in `self` that are NOT in `other`,
548
- using simple equality checks for dict/list items.
546
+ """Returns a new Domain containing items in `self` that are NOT in `other`.
547
+
548
+ Simple using equality checks for dict/list items.
549
549
 
550
550
  Args:
551
551
  other: The domain to compare with.