rasa-pro 3.13.7__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/stack/dialogue_stack.py +123 -2
  134. rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +57 -0
  135. rasa/dialogue_understanding/stack/utils.py +3 -2
  136. rasa/dialogue_understanding_test/du_test_runner.py +7 -2
  137. rasa/dialogue_understanding_test/du_test_schema.yml +3 -3
  138. rasa/e2e_test/e2e_test_runner.py +5 -0
  139. rasa/e2e_test/e2e_test_schema.yml +3 -3
  140. rasa/model_manager/model_api.py +1 -1
  141. rasa/model_manager/socket_bridge.py +8 -2
  142. rasa/server.py +10 -0
  143. rasa/shared/agents/__init__.py +0 -0
  144. rasa/shared/agents/utils.py +35 -0
  145. rasa/shared/constants.py +5 -0
  146. rasa/shared/core/constants.py +12 -1
  147. rasa/shared/core/domain.py +5 -5
  148. rasa/shared/core/events.py +319 -0
  149. rasa/shared/core/flows/flows_list.py +2 -2
  150. rasa/shared/core/flows/flows_yaml_schema.json +101 -186
  151. rasa/shared/core/flows/steps/call.py +51 -5
  152. rasa/shared/core/flows/validation.py +45 -7
  153. rasa/shared/core/flows/yaml_flows_io.py +3 -3
  154. rasa/shared/providers/llm/_base_litellm_client.py +39 -7
  155. rasa/shared/providers/llm/litellm_router_llm_client.py +8 -4
  156. rasa/shared/providers/llm/llm_client.py +7 -3
  157. rasa/shared/providers/llm/llm_response.py +49 -0
  158. rasa/shared/providers/llm/self_hosted_llm_client.py +8 -4
  159. rasa/shared/utils/common.py +2 -1
  160. rasa/shared/utils/llm.py +28 -5
  161. rasa/shared/utils/mcp/__init__.py +0 -0
  162. rasa/shared/utils/mcp/server_connection.py +157 -0
  163. rasa/shared/utils/schemas/events.py +42 -0
  164. rasa/studio/upload.py +4 -7
  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.7.dist-info → rasa_pro-3.14.0.dev1.dist-info}/METADATA +16 -15
  171. {rasa_pro-3.13.7.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.7.dist-info → rasa_pro-3.14.0.dev1.dist-info}/NOTICE +0 -0
  177. {rasa_pro-3.13.7.dist-info → rasa_pro-3.14.0.dev1.dist-info}/WHEEL +0 -0
  178. {rasa_pro-3.13.7.dist-info → rasa_pro-3.14.0.dev1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,275 @@
1
+ import importlib
2
+ import json
3
+ from typing import Any, Dict, List, Optional
4
+
5
+ import structlog
6
+
7
+ from rasa.agents.constants import (
8
+ KEY_CONTENT,
9
+ KEY_ROLE,
10
+ KEY_TOOL_CALL_ID,
11
+ TOOL_ADDITIONAL_PROPERTIES_KEY,
12
+ TOOL_DESCRIPTION_KEY,
13
+ TOOL_NAME_KEY,
14
+ TOOL_PARAMETERS_KEY,
15
+ TOOL_PROPERTIES_KEY,
16
+ TOOL_REQUIRED_KEY,
17
+ TOOL_STRICT_KEY,
18
+ TOOL_TYPE_FUNCTION_KEY,
19
+ TOOL_TYPE_KEY,
20
+ )
21
+ from rasa.agents.core.types import AgentStatus, ProtocolType
22
+ from rasa.agents.protocol.mcp.mcp_base_agent import MCPBaseAgent
23
+ from rasa.agents.schemas import (
24
+ AgentInput,
25
+ AgentOutput,
26
+ AgentToolResult,
27
+ )
28
+ from rasa.core.available_agents import AgentMCPServerConfig, ProtocolConfig
29
+ from rasa.shared.agents.utils import make_agent_identifier
30
+ from rasa.shared.constants import (
31
+ ROLE_TOOL,
32
+ )
33
+ from rasa.shared.providers.llm.llm_response import LLMResponse, LLMToolCall
34
+
35
+ DEFAULT_OPEN_AGENT_PROMPT_TEMPLATE = importlib.resources.read_text(
36
+ "rasa.agents.templates", "mcp_open_agent_prompt_template.jinja2"
37
+ )
38
+
39
+ KEY_TASK_COMPLETED = "task_completed"
40
+
41
+ TASK_COMPLETED_TOOL = {
42
+ TOOL_TYPE_KEY: TOOL_TYPE_FUNCTION_KEY,
43
+ TOOL_TYPE_FUNCTION_KEY: {
44
+ TOOL_NAME_KEY: KEY_TASK_COMPLETED,
45
+ TOOL_DESCRIPTION_KEY: "Signal that the MCP agent has FULLY completed its "
46
+ "primary task. Once you have presented your findings, follow-up with "
47
+ "a message summarizing the completed task in a comprehensive and well-written "
48
+ "manner. Avoid repeating information already provided in the conversation.",
49
+ TOOL_PARAMETERS_KEY: {
50
+ TOOL_TYPE_KEY: "object",
51
+ TOOL_PROPERTIES_KEY: {
52
+ "message": {
53
+ TOOL_TYPE_KEY: "string",
54
+ TOOL_DESCRIPTION_KEY: "A message describing the completed task.",
55
+ }
56
+ },
57
+ TOOL_REQUIRED_KEY: ["message"],
58
+ TOOL_ADDITIONAL_PROPERTIES_KEY: False,
59
+ },
60
+ TOOL_STRICT_KEY: True,
61
+ },
62
+ }
63
+
64
+ structlogger = structlog.get_logger()
65
+
66
+
67
+ class MCPOpenAgent(MCPBaseAgent):
68
+ """MCP protocol implementation."""
69
+
70
+ def __init__(
71
+ self,
72
+ name: str,
73
+ description: str,
74
+ protocol_type: ProtocolConfig,
75
+ server_configs: List[AgentMCPServerConfig],
76
+ llm_config: Optional[Dict[str, Any]] = None,
77
+ prompt_template: Optional[str] = None,
78
+ timeout: Optional[int] = None,
79
+ max_retries: Optional[int] = None,
80
+ ):
81
+ super().__init__(
82
+ name,
83
+ description,
84
+ protocol_type,
85
+ server_configs,
86
+ llm_config,
87
+ prompt_template,
88
+ timeout,
89
+ max_retries,
90
+ )
91
+
92
+ @property
93
+ def protocol_type(self) -> ProtocolType:
94
+ return ProtocolType.MCP_OPEN
95
+
96
+ @staticmethod
97
+ def get_default_prompt_template() -> str:
98
+ return DEFAULT_OPEN_AGENT_PROMPT_TEMPLATE
99
+
100
+ @staticmethod
101
+ def get_task_completed_tool() -> Dict[str, Any]:
102
+ """Get the task completed tool for MCP. Override to customize/disable."""
103
+ return TASK_COMPLETED_TOOL
104
+
105
+ @classmethod
106
+ def get_agent_specific_built_in_tools(
107
+ cls, agent_input: AgentInput
108
+ ) -> List[Dict[str, Any]]:
109
+ """Get agentic specific built-in tools."""
110
+ return [cls.get_task_completed_tool()]
111
+
112
+ def _run_task_completed_tool(
113
+ self,
114
+ tool_call: LLMToolCall,
115
+ agent_input: AgentInput,
116
+ tool_results: Dict[str, AgentToolResult],
117
+ ) -> AgentOutput:
118
+ """Run the task completed tool."""
119
+ # Create the agent tool result for the task completed tool.
120
+
121
+ tool_result = AgentToolResult(
122
+ tool_name=tool_call.tool_name,
123
+ result=tool_call.tool_args.get("message", "Task completed"),
124
+ )
125
+ tool_results[tool_call.id] = tool_result
126
+
127
+ # Create the agent output for the task completed tool.
128
+ return AgentOutput(
129
+ id=agent_input.id,
130
+ status=AgentStatus.COMPLETED,
131
+ response_message=tool_result.result,
132
+ tool_results=self._get_tool_results_for_agent_output(
133
+ agent_input, tool_results
134
+ ),
135
+ )
136
+
137
+ async def send_message(self, agent_input: AgentInput) -> AgentOutput:
138
+ """Send a message to the LLM and return the response."""
139
+ messages = self.build_messages_for_llm_request(agent_input)
140
+ tool_results: Dict[str, AgentToolResult] = {}
141
+ # Convert available tools to OpenAI JSON format
142
+ tools_in_openai_format = [
143
+ tool.to_openai_json_format()
144
+ for tool in self.get_available_tools(agent_input)
145
+ ]
146
+
147
+ for iteration in range(self.MAX_ITERATIONS):
148
+ try:
149
+ # Make the LLM call using the llm_client
150
+ structlogger.info(
151
+ "mcp_open_agent.send_message.sending_message_to_llm",
152
+ messages=messages,
153
+ event_info=f"Sending message to LLM (iteration {iteration + 1})",
154
+ agent_name=self._name,
155
+ agent_id=str(make_agent_identifier(self._name, self.protocol_type)),
156
+ )
157
+ llm_response = LLMResponse.ensure_llm_response(
158
+ await self.llm_client.acompletion(
159
+ messages, tools=tools_in_openai_format
160
+ )
161
+ )
162
+
163
+ # If no response from LLM, return an error output.
164
+ if llm_response is None or not (
165
+ llm_response.choices or llm_response.tool_calls
166
+ ):
167
+ event_info = "No response from LLM."
168
+ structlogger.warning(
169
+ "mcp_open_agent.send_message.no_llm_response",
170
+ event_info=event_info,
171
+ agent_name=self._name,
172
+ agent_id=str(
173
+ make_agent_identifier(self._name, self.protocol_type)
174
+ ),
175
+ )
176
+ return AgentOutput(
177
+ id=agent_input.id,
178
+ status=AgentStatus.RECOVERABLE_ERROR,
179
+ error_message=event_info,
180
+ tool_results=self._get_tool_results_for_agent_output(
181
+ agent_input, tool_results
182
+ ),
183
+ )
184
+
185
+ # If no tool calls, return the response directly with input required.
186
+ if not llm_response.tool_calls and len(llm_response.choices) == 1:
187
+ return AgentOutput(
188
+ id=agent_input.id,
189
+ status=AgentStatus.INPUT_REQUIRED,
190
+ response_message=llm_response.choices[0],
191
+ tool_results=self._get_tool_results_for_agent_output(
192
+ agent_input, tool_results
193
+ ),
194
+ )
195
+
196
+ # If there are tool calls, process them.
197
+ if llm_response.tool_calls:
198
+ # Add the assistant message with tool calls to the messages.
199
+ messages.append(
200
+ self._get_assistant_message_with_tool_calls(llm_response)
201
+ )
202
+ for tool_call in llm_response.tool_calls:
203
+ structlogger.info(
204
+ "mcp_open_agent.send_message.tool_call",
205
+ event_info=(
206
+ f"Processing tool call `{tool_call.tool_name}` with "
207
+ f"args: {tool_call.tool_args}"
208
+ ),
209
+ tool_name=tool_call.tool_name,
210
+ tool_args=json.dumps(tool_call.tool_args),
211
+ agent_name=self._name,
212
+ agent_id=str(
213
+ make_agent_identifier(self._name, self.protocol_type)
214
+ ),
215
+ )
216
+
217
+ # Agent signals task completion.
218
+ if tool_call.tool_name == KEY_TASK_COMPLETED:
219
+ return self._run_task_completed_tool(
220
+ tool_call, agent_input, tool_results
221
+ )
222
+
223
+ else:
224
+ # Execute the tool call.
225
+ tool_output = await self._execute_tool_call(
226
+ tool_call.tool_name,
227
+ tool_call.tool_args,
228
+ )
229
+
230
+ # If the tool call failed, generate an agent error output.
231
+ if tool_output.is_error or tool_output.result is None:
232
+ return self._generate_agent_error_output(
233
+ tool_output, agent_input, tool_call
234
+ )
235
+
236
+ # Store the tool output in the tool_results.
237
+ tool_results[tool_call.id] = tool_output
238
+
239
+ # Add the tool call message to the messages.
240
+ messages.append(
241
+ {
242
+ KEY_ROLE: ROLE_TOOL,
243
+ KEY_TOOL_CALL_ID: tool_call.id,
244
+ KEY_CONTENT: tool_output.result,
245
+ }
246
+ )
247
+
248
+ except Exception as e:
249
+ structlogger.error(
250
+ "mcp_open_agent.send_message.error_in_agent_loop",
251
+ event_info=f"Failed to send message: {e}",
252
+ user_message=agent_input.user_message,
253
+ agent_name=self._name,
254
+ agent_id=str(make_agent_identifier(self._name, self.protocol_type)),
255
+ )
256
+ return AgentOutput(
257
+ id=agent_input.id,
258
+ status=AgentStatus.FATAL_ERROR,
259
+ response_message=f"I encountered an error: {e!s}",
260
+ tool_results=self._get_tool_results_for_agent_output(
261
+ agent_input, tool_results
262
+ ),
263
+ error_message=str(e),
264
+ )
265
+ return AgentOutput(
266
+ id=agent_input.id,
267
+ status=AgentStatus.COMPLETED,
268
+ response_message=(
269
+ "I've completed my research but couldn't provide a final answer within"
270
+ "the allowed steps."
271
+ ),
272
+ tool_results=self._get_tool_results_for_agent_output(
273
+ agent_input, tool_results
274
+ ),
275
+ )