fast-agent-mcp 0.2.58__py3-none-any.whl → 0.3.0__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 fast-agent-mcp might be problematic. Click here for more details.

Files changed (233) hide show
  1. fast_agent/__init__.py +127 -0
  2. fast_agent/agents/__init__.py +36 -0
  3. {mcp_agent/core → fast_agent/agents}/agent_types.py +2 -1
  4. fast_agent/agents/llm_agent.py +217 -0
  5. fast_agent/agents/llm_decorator.py +486 -0
  6. mcp_agent/agents/base_agent.py → fast_agent/agents/mcp_agent.py +377 -385
  7. fast_agent/agents/tool_agent.py +168 -0
  8. {mcp_agent → fast_agent}/agents/workflow/chain_agent.py +43 -33
  9. {mcp_agent → fast_agent}/agents/workflow/evaluator_optimizer.py +31 -35
  10. {mcp_agent → fast_agent}/agents/workflow/iterative_planner.py +56 -47
  11. {mcp_agent → fast_agent}/agents/workflow/orchestrator_models.py +4 -4
  12. {mcp_agent → fast_agent}/agents/workflow/parallel_agent.py +34 -41
  13. {mcp_agent → fast_agent}/agents/workflow/router_agent.py +54 -39
  14. {mcp_agent → fast_agent}/cli/__main__.py +5 -3
  15. {mcp_agent → fast_agent}/cli/commands/check_config.py +95 -66
  16. {mcp_agent → fast_agent}/cli/commands/go.py +20 -11
  17. {mcp_agent → fast_agent}/cli/commands/quickstart.py +4 -4
  18. {mcp_agent → fast_agent}/cli/commands/server_helpers.py +1 -1
  19. {mcp_agent → fast_agent}/cli/commands/setup.py +64 -134
  20. {mcp_agent → fast_agent}/cli/commands/url_parser.py +9 -8
  21. {mcp_agent → fast_agent}/cli/main.py +36 -16
  22. {mcp_agent → fast_agent}/cli/terminal.py +2 -2
  23. {mcp_agent → fast_agent}/config.py +10 -2
  24. fast_agent/constants.py +8 -0
  25. {mcp_agent → fast_agent}/context.py +24 -19
  26. {mcp_agent → fast_agent}/context_dependent.py +9 -5
  27. fast_agent/core/__init__.py +17 -0
  28. {mcp_agent → fast_agent}/core/agent_app.py +39 -36
  29. fast_agent/core/core_app.py +135 -0
  30. {mcp_agent → fast_agent}/core/direct_decorators.py +12 -26
  31. {mcp_agent → fast_agent}/core/direct_factory.py +95 -73
  32. {mcp_agent → fast_agent/core}/executor/executor.py +4 -5
  33. {mcp_agent → fast_agent}/core/fastagent.py +32 -32
  34. fast_agent/core/logging/__init__.py +5 -0
  35. {mcp_agent → fast_agent/core}/logging/events.py +3 -3
  36. {mcp_agent → fast_agent/core}/logging/json_serializer.py +1 -1
  37. {mcp_agent → fast_agent/core}/logging/listeners.py +85 -7
  38. {mcp_agent → fast_agent/core}/logging/logger.py +7 -7
  39. {mcp_agent → fast_agent/core}/logging/transport.py +10 -11
  40. fast_agent/core/prompt.py +9 -0
  41. {mcp_agent → fast_agent}/core/validation.py +4 -4
  42. fast_agent/event_progress.py +61 -0
  43. fast_agent/history/history_exporter.py +44 -0
  44. {mcp_agent → fast_agent}/human_input/__init__.py +9 -12
  45. {mcp_agent → fast_agent}/human_input/elicitation_handler.py +26 -8
  46. {mcp_agent → fast_agent}/human_input/elicitation_state.py +7 -7
  47. {mcp_agent → fast_agent}/human_input/simple_form.py +6 -4
  48. {mcp_agent → fast_agent}/human_input/types.py +1 -18
  49. fast_agent/interfaces.py +228 -0
  50. fast_agent/llm/__init__.py +9 -0
  51. mcp_agent/llm/augmented_llm.py → fast_agent/llm/fastagent_llm.py +127 -218
  52. fast_agent/llm/internal/passthrough.py +137 -0
  53. mcp_agent/llm/augmented_llm_playback.py → fast_agent/llm/internal/playback.py +29 -25
  54. mcp_agent/llm/augmented_llm_silent.py → fast_agent/llm/internal/silent.py +10 -17
  55. fast_agent/llm/internal/slow.py +38 -0
  56. {mcp_agent → fast_agent}/llm/memory.py +40 -30
  57. {mcp_agent → fast_agent}/llm/model_database.py +35 -2
  58. {mcp_agent → fast_agent}/llm/model_factory.py +103 -77
  59. fast_agent/llm/model_info.py +126 -0
  60. {mcp_agent/llm/providers → fast_agent/llm/provider/anthropic}/anthropic_utils.py +7 -7
  61. fast_agent/llm/provider/anthropic/llm_anthropic.py +603 -0
  62. {mcp_agent/llm/providers → fast_agent/llm/provider/anthropic}/multipart_converter_anthropic.py +79 -86
  63. {mcp_agent/llm/providers → fast_agent/llm/provider/bedrock}/bedrock_utils.py +3 -1
  64. mcp_agent/llm/providers/augmented_llm_bedrock.py → fast_agent/llm/provider/bedrock/llm_bedrock.py +833 -717
  65. {mcp_agent/llm/providers → fast_agent/llm/provider/google}/google_converter.py +66 -14
  66. fast_agent/llm/provider/google/llm_google_native.py +431 -0
  67. mcp_agent/llm/providers/augmented_llm_aliyun.py → fast_agent/llm/provider/openai/llm_aliyun.py +6 -7
  68. mcp_agent/llm/providers/augmented_llm_azure.py → fast_agent/llm/provider/openai/llm_azure.py +4 -4
  69. mcp_agent/llm/providers/augmented_llm_deepseek.py → fast_agent/llm/provider/openai/llm_deepseek.py +10 -11
  70. mcp_agent/llm/providers/augmented_llm_generic.py → fast_agent/llm/provider/openai/llm_generic.py +4 -4
  71. mcp_agent/llm/providers/augmented_llm_google_oai.py → fast_agent/llm/provider/openai/llm_google_oai.py +4 -4
  72. mcp_agent/llm/providers/augmented_llm_groq.py → fast_agent/llm/provider/openai/llm_groq.py +14 -16
  73. mcp_agent/llm/providers/augmented_llm_openai.py → fast_agent/llm/provider/openai/llm_openai.py +133 -207
  74. mcp_agent/llm/providers/augmented_llm_openrouter.py → fast_agent/llm/provider/openai/llm_openrouter.py +6 -6
  75. mcp_agent/llm/providers/augmented_llm_tensorzero_openai.py → fast_agent/llm/provider/openai/llm_tensorzero_openai.py +17 -16
  76. mcp_agent/llm/providers/augmented_llm_xai.py → fast_agent/llm/provider/openai/llm_xai.py +6 -6
  77. {mcp_agent/llm/providers → fast_agent/llm/provider/openai}/multipart_converter_openai.py +125 -63
  78. {mcp_agent/llm/providers → fast_agent/llm/provider/openai}/openai_multipart.py +12 -12
  79. {mcp_agent/llm/providers → fast_agent/llm/provider/openai}/openai_utils.py +18 -16
  80. {mcp_agent → fast_agent}/llm/provider_key_manager.py +2 -2
  81. {mcp_agent → fast_agent}/llm/provider_types.py +2 -0
  82. {mcp_agent → fast_agent}/llm/sampling_converter.py +15 -12
  83. {mcp_agent → fast_agent}/llm/usage_tracking.py +23 -5
  84. fast_agent/mcp/__init__.py +43 -0
  85. {mcp_agent → fast_agent}/mcp/elicitation_factory.py +3 -3
  86. {mcp_agent → fast_agent}/mcp/elicitation_handlers.py +19 -10
  87. {mcp_agent → fast_agent}/mcp/gen_client.py +3 -3
  88. fast_agent/mcp/helpers/__init__.py +36 -0
  89. fast_agent/mcp/helpers/content_helpers.py +183 -0
  90. {mcp_agent → fast_agent}/mcp/helpers/server_config_helpers.py +8 -8
  91. {mcp_agent → fast_agent}/mcp/hf_auth.py +25 -23
  92. fast_agent/mcp/interfaces.py +93 -0
  93. {mcp_agent → fast_agent}/mcp/logger_textio.py +4 -4
  94. {mcp_agent → fast_agent}/mcp/mcp_agent_client_session.py +49 -44
  95. {mcp_agent → fast_agent}/mcp/mcp_aggregator.py +66 -115
  96. {mcp_agent → fast_agent}/mcp/mcp_connection_manager.py +16 -23
  97. {mcp_agent/core → fast_agent/mcp}/mcp_content.py +23 -15
  98. {mcp_agent → fast_agent}/mcp/mime_utils.py +39 -0
  99. fast_agent/mcp/prompt.py +159 -0
  100. mcp_agent/mcp/prompt_message_multipart.py → fast_agent/mcp/prompt_message_extended.py +27 -20
  101. {mcp_agent → fast_agent}/mcp/prompt_render.py +21 -19
  102. {mcp_agent → fast_agent}/mcp/prompt_serialization.py +46 -46
  103. fast_agent/mcp/prompts/__main__.py +7 -0
  104. {mcp_agent → fast_agent}/mcp/prompts/prompt_helpers.py +31 -30
  105. {mcp_agent → fast_agent}/mcp/prompts/prompt_load.py +8 -8
  106. {mcp_agent → fast_agent}/mcp/prompts/prompt_server.py +11 -19
  107. {mcp_agent → fast_agent}/mcp/prompts/prompt_template.py +18 -18
  108. {mcp_agent → fast_agent}/mcp/resource_utils.py +1 -1
  109. {mcp_agent → fast_agent}/mcp/sampling.py +31 -26
  110. {mcp_agent/mcp_server → fast_agent/mcp/server}/__init__.py +1 -1
  111. {mcp_agent/mcp_server → fast_agent/mcp/server}/agent_server.py +5 -6
  112. fast_agent/mcp/ui_agent.py +48 -0
  113. fast_agent/mcp/ui_mixin.py +209 -0
  114. fast_agent/mcp_server_registry.py +90 -0
  115. {mcp_agent → fast_agent}/resources/examples/data-analysis/analysis-campaign.py +5 -4
  116. {mcp_agent → fast_agent}/resources/examples/data-analysis/analysis.py +1 -1
  117. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/forms_demo.py +3 -3
  118. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/game_character.py +2 -2
  119. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/game_character_handler.py +1 -1
  120. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/tool_call.py +1 -1
  121. {mcp_agent → fast_agent}/resources/examples/mcp/state-transfer/agent_one.py +1 -1
  122. {mcp_agent → fast_agent}/resources/examples/mcp/state-transfer/agent_two.py +1 -1
  123. {mcp_agent → fast_agent}/resources/examples/researcher/researcher-eval.py +1 -1
  124. {mcp_agent → fast_agent}/resources/examples/researcher/researcher-imp.py +1 -1
  125. {mcp_agent → fast_agent}/resources/examples/researcher/researcher.py +1 -1
  126. {mcp_agent → fast_agent}/resources/examples/tensorzero/agent.py +2 -2
  127. {mcp_agent → fast_agent}/resources/examples/tensorzero/image_demo.py +3 -3
  128. {mcp_agent → fast_agent}/resources/examples/tensorzero/simple_agent.py +1 -1
  129. {mcp_agent → fast_agent}/resources/examples/workflows/chaining.py +1 -1
  130. {mcp_agent → fast_agent}/resources/examples/workflows/evaluator.py +3 -3
  131. {mcp_agent → fast_agent}/resources/examples/workflows/human_input.py +5 -3
  132. {mcp_agent → fast_agent}/resources/examples/workflows/orchestrator.py +1 -1
  133. {mcp_agent → fast_agent}/resources/examples/workflows/parallel.py +2 -2
  134. {mcp_agent → fast_agent}/resources/examples/workflows/router.py +5 -2
  135. fast_agent/resources/setup/.gitignore +24 -0
  136. fast_agent/resources/setup/agent.py +18 -0
  137. fast_agent/resources/setup/fastagent.config.yaml +44 -0
  138. fast_agent/resources/setup/fastagent.secrets.yaml.example +38 -0
  139. fast_agent/tools/elicitation.py +369 -0
  140. fast_agent/types/__init__.py +32 -0
  141. fast_agent/types/llm_stop_reason.py +77 -0
  142. fast_agent/ui/__init__.py +38 -0
  143. fast_agent/ui/console_display.py +1005 -0
  144. {mcp_agent/human_input → fast_agent/ui}/elicitation_form.py +17 -12
  145. mcp_agent/human_input/elicitation_forms.py → fast_agent/ui/elicitation_style.py +1 -1
  146. {mcp_agent/core → fast_agent/ui}/enhanced_prompt.py +96 -25
  147. {mcp_agent/core → fast_agent/ui}/interactive_prompt.py +330 -125
  148. fast_agent/ui/mcp_ui_utils.py +224 -0
  149. {mcp_agent → fast_agent/ui}/progress_display.py +2 -2
  150. {mcp_agent/logging → fast_agent/ui}/rich_progress.py +4 -4
  151. {mcp_agent/core → fast_agent/ui}/usage_display.py +3 -8
  152. {fast_agent_mcp-0.2.58.dist-info → fast_agent_mcp-0.3.0.dist-info}/METADATA +7 -7
  153. fast_agent_mcp-0.3.0.dist-info/RECORD +202 -0
  154. fast_agent_mcp-0.3.0.dist-info/entry_points.txt +5 -0
  155. fast_agent_mcp-0.2.58.dist-info/RECORD +0 -193
  156. fast_agent_mcp-0.2.58.dist-info/entry_points.txt +0 -6
  157. mcp_agent/__init__.py +0 -114
  158. mcp_agent/agents/agent.py +0 -92
  159. mcp_agent/agents/workflow/__init__.py +0 -1
  160. mcp_agent/agents/workflow/orchestrator_agent.py +0 -597
  161. mcp_agent/app.py +0 -175
  162. mcp_agent/core/__init__.py +0 -26
  163. mcp_agent/core/prompt.py +0 -191
  164. mcp_agent/event_progress.py +0 -134
  165. mcp_agent/human_input/handler.py +0 -81
  166. mcp_agent/llm/__init__.py +0 -2
  167. mcp_agent/llm/augmented_llm_passthrough.py +0 -232
  168. mcp_agent/llm/augmented_llm_slow.py +0 -53
  169. mcp_agent/llm/providers/__init__.py +0 -8
  170. mcp_agent/llm/providers/augmented_llm_anthropic.py +0 -718
  171. mcp_agent/llm/providers/augmented_llm_google_native.py +0 -496
  172. mcp_agent/llm/providers/sampling_converter_anthropic.py +0 -57
  173. mcp_agent/llm/providers/sampling_converter_openai.py +0 -26
  174. mcp_agent/llm/sampling_format_converter.py +0 -37
  175. mcp_agent/logging/__init__.py +0 -0
  176. mcp_agent/mcp/__init__.py +0 -50
  177. mcp_agent/mcp/helpers/__init__.py +0 -25
  178. mcp_agent/mcp/helpers/content_helpers.py +0 -187
  179. mcp_agent/mcp/interfaces.py +0 -266
  180. mcp_agent/mcp/prompts/__init__.py +0 -0
  181. mcp_agent/mcp/prompts/__main__.py +0 -10
  182. mcp_agent/mcp_server_registry.py +0 -343
  183. mcp_agent/tools/tool_definition.py +0 -14
  184. mcp_agent/ui/console_display.py +0 -790
  185. mcp_agent/ui/console_display_legacy.py +0 -401
  186. {mcp_agent → fast_agent}/agents/workflow/orchestrator_prompts.py +0 -0
  187. {mcp_agent/agents → fast_agent/cli}/__init__.py +0 -0
  188. {mcp_agent → fast_agent}/cli/constants.py +0 -0
  189. {mcp_agent → fast_agent}/core/error_handling.py +0 -0
  190. {mcp_agent → fast_agent}/core/exceptions.py +0 -0
  191. {mcp_agent/cli → fast_agent/core/executor}/__init__.py +0 -0
  192. {mcp_agent → fast_agent/core}/executor/task_registry.py +0 -0
  193. {mcp_agent → fast_agent/core}/executor/workflow_signal.py +0 -0
  194. {mcp_agent → fast_agent}/human_input/form_fields.py +0 -0
  195. {mcp_agent → fast_agent}/llm/prompt_utils.py +0 -0
  196. {mcp_agent/core → fast_agent/llm}/request_params.py +0 -0
  197. {mcp_agent → fast_agent}/mcp/common.py +0 -0
  198. {mcp_agent/executor → fast_agent/mcp/prompts}/__init__.py +0 -0
  199. {mcp_agent → fast_agent}/mcp/prompts/prompt_constants.py +0 -0
  200. {mcp_agent → fast_agent}/py.typed +0 -0
  201. {mcp_agent → fast_agent}/resources/examples/data-analysis/fastagent.config.yaml +0 -0
  202. {mcp_agent → fast_agent}/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +0 -0
  203. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/elicitation_account_server.py +0 -0
  204. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/elicitation_forms_server.py +0 -0
  205. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/elicitation_game_server.py +0 -0
  206. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/fastagent.config.yaml +0 -0
  207. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/fastagent.secrets.yaml.example +0 -0
  208. {mcp_agent → fast_agent}/resources/examples/mcp/state-transfer/fastagent.config.yaml +0 -0
  209. {mcp_agent → fast_agent}/resources/examples/mcp/state-transfer/fastagent.secrets.yaml.example +0 -0
  210. {mcp_agent → fast_agent}/resources/examples/researcher/fastagent.config.yaml +0 -0
  211. {mcp_agent → fast_agent}/resources/examples/tensorzero/.env.sample +0 -0
  212. {mcp_agent → fast_agent}/resources/examples/tensorzero/Makefile +0 -0
  213. {mcp_agent → fast_agent}/resources/examples/tensorzero/README.md +0 -0
  214. {mcp_agent → fast_agent}/resources/examples/tensorzero/demo_images/clam.jpg +0 -0
  215. {mcp_agent → fast_agent}/resources/examples/tensorzero/demo_images/crab.png +0 -0
  216. {mcp_agent → fast_agent}/resources/examples/tensorzero/demo_images/shrimp.png +0 -0
  217. {mcp_agent → fast_agent}/resources/examples/tensorzero/docker-compose.yml +0 -0
  218. {mcp_agent → fast_agent}/resources/examples/tensorzero/fastagent.config.yaml +0 -0
  219. {mcp_agent → fast_agent}/resources/examples/tensorzero/mcp_server/Dockerfile +0 -0
  220. {mcp_agent → fast_agent}/resources/examples/tensorzero/mcp_server/entrypoint.sh +0 -0
  221. {mcp_agent → fast_agent}/resources/examples/tensorzero/mcp_server/mcp_server.py +0 -0
  222. {mcp_agent → fast_agent}/resources/examples/tensorzero/mcp_server/pyproject.toml +0 -0
  223. {mcp_agent → fast_agent}/resources/examples/tensorzero/tensorzero_config/system_schema.json +0 -0
  224. {mcp_agent → fast_agent}/resources/examples/tensorzero/tensorzero_config/system_template.minijinja +0 -0
  225. {mcp_agent → fast_agent}/resources/examples/tensorzero/tensorzero_config/tensorzero.toml +0 -0
  226. {mcp_agent → fast_agent}/resources/examples/workflows/fastagent.config.yaml +0 -0
  227. {mcp_agent → fast_agent}/resources/examples/workflows/graded_report.md +0 -0
  228. {mcp_agent → fast_agent}/resources/examples/workflows/short_story.md +0 -0
  229. {mcp_agent → fast_agent}/resources/examples/workflows/short_story.txt +0 -0
  230. {mcp_agent → fast_agent/ui}/console.py +0 -0
  231. {mcp_agent/core → fast_agent/ui}/mermaid_utils.py +0 -0
  232. {fast_agent_mcp-0.2.58.dist-info → fast_agent_mcp-0.3.0.dist-info}/WHEEL +0 -0
  233. {fast_agent_mcp-0.2.58.dist-info → fast_agent_mcp-0.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,597 +0,0 @@
1
- """
2
- OrchestratorAgent implementation using the BaseAgent adapter pattern.
3
-
4
- This workflow provides an implementation that manages complex tasks by
5
- dynamically planning, delegating to specialized agents, and synthesizing results.
6
- """
7
-
8
- from typing import Any, Dict, List, Literal, Optional, Tuple, Type
9
-
10
- from mcp.types import TextContent
11
-
12
- from mcp_agent.agents.agent import Agent
13
- from mcp_agent.agents.base_agent import BaseAgent
14
- from mcp_agent.agents.workflow.orchestrator_models import (
15
- Plan,
16
- PlanningStep,
17
- PlanResult,
18
- Step,
19
- TaskWithResult,
20
- format_plan_result,
21
- format_step_result_text,
22
- )
23
- from mcp_agent.agents.workflow.orchestrator_prompts import (
24
- FULL_PLAN_PROMPT_TEMPLATE,
25
- ITERATIVE_PLAN_PROMPT_TEMPLATE,
26
- SYNTHESIZE_INCOMPLETE_PLAN_TEMPLATE,
27
- SYNTHESIZE_PLAN_PROMPT_TEMPLATE,
28
- TASK_PROMPT_TEMPLATE,
29
- )
30
- from mcp_agent.core.agent_types import AgentConfig, AgentType
31
- from mcp_agent.core.exceptions import AgentConfigError
32
- from mcp_agent.core.prompt import Prompt
33
- from mcp_agent.core.request_params import RequestParams
34
- from mcp_agent.logging.logger import get_logger
35
- from mcp_agent.mcp.interfaces import ModelT
36
- from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
37
-
38
- logger = get_logger(__name__)
39
-
40
-
41
- class OrchestratorAgent(BaseAgent):
42
- """
43
- An agent that implements the orchestrator workflow pattern.
44
-
45
- Dynamically creates execution plans and delegates tasks
46
- to specialized worker agents, synthesizing their results into a cohesive output.
47
- Supports both full planning and iterative planning modes.
48
- """
49
-
50
- @property
51
- def agent_type(self) -> AgentType:
52
- """Return the type of this agent."""
53
- return AgentType.ORCHESTRATOR
54
-
55
- def __init__(
56
- self,
57
- config: AgentConfig,
58
- agents: List[Agent],
59
- plan_type: Literal["full", "iterative"] = "full",
60
- plan_iterations: int = 5,
61
- context: Optional[Any] = None,
62
- **kwargs,
63
- ) -> None:
64
- """
65
- Initialize an OrchestratorAgent.
66
-
67
- Args:
68
- config: Agent configuration or name
69
- agents: List of specialized worker agents available for task execution
70
- plan_type: Planning mode ("full" or "iterative")
71
- context: Optional context object
72
- **kwargs: Additional keyword arguments to pass to BaseAgent
73
- """
74
- super().__init__(config, context=context, **kwargs)
75
-
76
- if not agents:
77
- raise AgentConfigError("At least one worker agent must be provided")
78
-
79
- self.plan_type = plan_type
80
-
81
- # Store agents by name for easier lookup
82
- self.agents: Dict[str, Agent] = {}
83
- for agent in agents:
84
- agent_name = agent.name
85
- self.logger.info(f"Adding agent '{agent_name}' to orchestrator")
86
- self.agents[agent_name] = agent
87
- self.plan_iterations = plan_iterations
88
- # For tracking state during execution
89
- self.plan_result: Optional[PlanResult] = None
90
-
91
- async def generate(
92
- self,
93
- multipart_messages: List[PromptMessageMultipart],
94
- request_params: Optional[RequestParams] = None,
95
- ) -> PromptMessageMultipart:
96
- """
97
- Execute an orchestrated plan to process the input.
98
-
99
- Args:
100
- multipart_messages: Messages to process
101
- request_params: Optional request parameters
102
-
103
- Returns:
104
- The final synthesized response from the orchestration
105
- """
106
- # Extract user request
107
- objective = multipart_messages[-1].all_text() if multipart_messages else ""
108
-
109
- # Initialize execution parameters
110
- params = self._merge_request_params(request_params)
111
-
112
- # Execute the plan
113
- plan_result = await self._execute_plan(objective, params)
114
- self.plan_result = plan_result
115
-
116
- # Return the result
117
- return PromptMessageMultipart(
118
- role="assistant",
119
- content=[TextContent(type="text", text=plan_result.result or "No result available")],
120
- )
121
-
122
- async def structured(
123
- self,
124
- prompt: List[PromptMessageMultipart],
125
- model: Type[ModelT],
126
- request_params: Optional[RequestParams] = None,
127
- ) -> Tuple[ModelT | None, PromptMessageMultipart]:
128
- """
129
- Execute an orchestration plan and parse the result into a structured format.
130
-
131
- Args:
132
- prompt: List of messages to process
133
- model: Pydantic model to parse the response into
134
- request_params: Optional request parameters
135
-
136
- Returns:
137
- The parsed final response, or None if parsing fails
138
- """
139
- # Generate orchestration result
140
- response = await self.generate(prompt, request_params)
141
-
142
- # Try to parse the response into the specified model
143
- try:
144
- result_text = response.all_text()
145
- prompt_message = PromptMessageMultipart(
146
- role="user", content=[TextContent(type="text", text=result_text)]
147
- )
148
- assert self._llm
149
- return await self._llm.structured([prompt_message], model, request_params)
150
- except Exception as e:
151
- self.logger.warning(f"Failed to parse orchestration result: {str(e)}")
152
- return None, Prompt.assistant(f"Failed to parse orchestration result: {str(e)}")
153
-
154
- async def initialize(self) -> None:
155
- """Initialize the orchestrator agent and worker agents."""
156
- await super().initialize()
157
-
158
- # Initialize all worker agents if not already initialized
159
- for agent_name, agent in self.agents.items():
160
- if not getattr(agent, "initialized", False):
161
- self.logger.debug(f"Initializing agent: {agent_name}")
162
- await agent.initialize()
163
-
164
- self.initialized = True
165
-
166
- async def shutdown(self) -> None:
167
- """Shutdown the orchestrator agent and worker agents."""
168
- await super().shutdown()
169
-
170
- # Shutdown all worker agents
171
- for agent_name, agent in self.agents.items():
172
- try:
173
- await agent.shutdown()
174
- except Exception as e:
175
- self.logger.warning(f"Error shutting down agent {agent_name}: {str(e)}")
176
-
177
- async def _execute_plan(self, objective: str, request_params: RequestParams) -> PlanResult:
178
- """
179
- Execute a plan to achieve the given objective.
180
-
181
- Args:
182
- objective: The objective to achieve
183
- request_params: Request parameters for execution
184
-
185
- Returns:
186
- PlanResult containing execution results and final output
187
- """
188
- iterations = 0
189
- total_steps_executed = 0
190
- max_iterations = self.plan_iterations
191
- max_steps = getattr(request_params, "max_steps", max_iterations * 3)
192
-
193
- # Initialize plan result
194
- plan_result = PlanResult(objective=objective, step_results=[])
195
- plan_result.max_iterations_reached = False
196
-
197
- while iterations < max_iterations:
198
- # Generate plan based on planning mode
199
- if self.plan_type == "iterative":
200
- next_step = await self._get_next_step(objective, plan_result, request_params)
201
- if next_step is None:
202
- self.logger.error("Failed to generate next step, ending iteration early")
203
- plan_result.max_iterations_reached = True
204
- break
205
-
206
- logger.debug(f"Iteration {iterations}: Iterative plan:", data=next_step)
207
- plan = Plan(steps=[next_step], is_complete=next_step.is_complete)
208
- elif self.plan_type == "full":
209
- plan = await self._get_full_plan(objective, plan_result, request_params)
210
- if plan is None:
211
- self.logger.error("Failed to generate full plan, ending iteration early")
212
- plan_result.max_iterations_reached = True
213
- break
214
-
215
- logger.debug(f"Iteration {iterations}: Full Plan:", data=plan)
216
- else:
217
- raise ValueError(f"Invalid plan type: {self.plan_type}")
218
-
219
- # Validate agent names early
220
- self._validate_agent_names(plan)
221
-
222
- # Store plan in result
223
- plan_result.plan = plan
224
-
225
- # Execute the steps in the plan
226
- for step in plan.steps:
227
- # Check if we've hit the step limit
228
- if total_steps_executed >= max_steps:
229
- self.logger.warning(
230
- f"Reached maximum step limit ({max_steps}) without completing objective"
231
- )
232
- plan_result.max_iterations_reached = True
233
- break
234
-
235
- # Execute the step and collect results
236
- step_result = await self._execute_step(step, plan_result, request_params)
237
-
238
- plan_result.add_step_result(step_result)
239
- total_steps_executed += 1
240
-
241
- # Check if we need to break due to hitting max steps
242
- if getattr(plan_result, "max_iterations_reached", False):
243
- break
244
-
245
- # If the plan is marked complete, finalize the result
246
- if plan.is_complete:
247
- plan_result.is_complete = True
248
- break
249
-
250
- # Increment iteration counter
251
- iterations += 1
252
-
253
- # Generate final result based on execution status
254
- if iterations >= max_iterations and not plan_result.is_complete:
255
- self.logger.warning(f"Failed to complete in {max_iterations} iterations")
256
- plan_result.max_iterations_reached = True
257
-
258
- # Use incomplete plan template
259
- synthesis_prompt = SYNTHESIZE_INCOMPLETE_PLAN_TEMPLATE.format(
260
- plan_result=format_plan_result(plan_result), max_iterations=max_iterations
261
- )
262
- else:
263
- # Either plan is complete or we had other limits
264
- if not plan_result.is_complete:
265
- plan_result.is_complete = True
266
-
267
- # Use standard template
268
- synthesis_prompt = SYNTHESIZE_PLAN_PROMPT_TEMPLATE.format(
269
- plan_result=format_plan_result(plan_result)
270
- )
271
-
272
- # Generate final synthesis
273
- plan_result.result = await self._planner_generate_str(
274
- synthesis_prompt, request_params.model_copy(update={"max_iterations": 1})
275
- )
276
-
277
- return plan_result
278
-
279
- async def _execute_step(
280
- self, step: Step, previous_result: PlanResult, request_params: RequestParams
281
- ) -> Any:
282
- """
283
- Execute a single step from the plan.
284
-
285
- Args:
286
- step: The step to execute
287
- previous_result: Results of the plan execution so far
288
- request_params: Request parameters
289
-
290
- Returns:
291
- Result of executing the step
292
- """
293
- from mcp_agent.agents.workflow.orchestrator_models import StepResult
294
-
295
- # Initialize step result
296
- step_result = StepResult(step=step, task_results=[])
297
-
298
- # Format context for tasks
299
- context = format_plan_result(previous_result)
300
-
301
- # Execute all tasks in parallel
302
- futures = []
303
- error_tasks = []
304
-
305
- for task in step.tasks:
306
- # Check agent exists
307
- agent = self.agents.get(task.agent)
308
- if not agent:
309
- self.logger.error(
310
- f"No agent found matching '{task.agent}'. Available agents: {list(self.agents.keys())}"
311
- )
312
- error_tasks.append(
313
- (
314
- task,
315
- f"Error: Agent '{task.agent}' not found. Available agents: {', '.join(self.agents.keys())}",
316
- )
317
- )
318
- continue
319
-
320
- # Prepare task prompt
321
- task_description = TASK_PROMPT_TEMPLATE.format(
322
- objective=previous_result.objective, task=task.description, context=context
323
- )
324
-
325
- # Queue task for execution
326
- futures.append(
327
- (
328
- task,
329
- agent.generate(
330
- [
331
- PromptMessageMultipart(
332
- role="user",
333
- content=[TextContent(type="text", text=task_description)],
334
- )
335
- ]
336
- ),
337
- )
338
- )
339
-
340
- # Wait for all tasks
341
- task_results = []
342
- for future in futures:
343
- task, future_obj = future
344
- try:
345
- result = await future_obj
346
- result_text = result.all_text()
347
-
348
- # Create task result
349
- task_model = task.model_dump()
350
- task_result = TaskWithResult(
351
- description=task_model["description"],
352
- agent=task_model["agent"],
353
- result=result_text,
354
- )
355
- task_results.append(task_result)
356
- except Exception as e:
357
- self.logger.error(f"Error executing task: {str(e)}")
358
- # Add error result
359
- task_model = task.model_dump()
360
- task_results.append(
361
- TaskWithResult(
362
- description=task_model["description"],
363
- agent=task_model["agent"],
364
- result=f"ERROR: {str(e)}",
365
- )
366
- )
367
-
368
- # Add all task results to step result
369
- for task_result in task_results:
370
- step_result.add_task_result(task_result)
371
-
372
- # Add error task results
373
- for task, error_message in error_tasks:
374
- task_model = task.model_dump()
375
- step_result.add_task_result(
376
- TaskWithResult(
377
- description=task_model["description"],
378
- agent=task_model["agent"],
379
- result=f"ERROR: {error_message}",
380
- )
381
- )
382
-
383
- # Format step result
384
- step_result.result = format_step_result_text(step_result)
385
- return step_result
386
-
387
- async def _get_full_plan(
388
- self, objective: str, plan_result: PlanResult, request_params: RequestParams
389
- ) -> Optional[Plan]:
390
- """
391
- Generate a full plan with all steps.
392
-
393
- Args:
394
- objective: The objective to achieve
395
- plan_result: Current plan execution state
396
- request_params: Request parameters
397
-
398
- Returns:
399
- Complete Plan with all steps, or None if parsing fails
400
- """
401
- # Format agent information for the prompt
402
- agent_formats = []
403
- for agent_name in self.agents.keys():
404
- formatted = self._format_agent_info(agent_name)
405
- agent_formats.append(formatted)
406
-
407
- agents = "\n".join(agent_formats)
408
-
409
- # Determine plan status
410
- if plan_result.is_complete:
411
- plan_status = "Plan Status: Complete"
412
- elif plan_result.step_results:
413
- plan_status = "Plan Status: In Progress"
414
- else:
415
- plan_status = "Plan Status: Not Started"
416
-
417
- # Calculate iteration information
418
- max_iterations = self.plan_iterations
419
- current_iteration = len(plan_result.step_results)
420
- current_iteration = min(current_iteration, max_iterations - 1)
421
- iterations_remaining = max(0, max_iterations - current_iteration - 1)
422
- iterations_info = f"Planning Budget: Iteration {current_iteration + 1} of {max_iterations} (with {iterations_remaining} remaining)"
423
-
424
- # Format the planning prompt
425
- prompt = FULL_PLAN_PROMPT_TEMPLATE.format(
426
- objective=objective,
427
- plan_result=format_plan_result(plan_result),
428
- plan_status=plan_status,
429
- iterations_info=iterations_info,
430
- agents=agents,
431
- )
432
-
433
- # Get structured response from LLM
434
- try:
435
- plan_msg = PromptMessageMultipart(
436
- role="user", content=[TextContent(type="text", text=prompt)]
437
- )
438
- plan, _ = await self._llm.structured([plan_msg], Plan, request_params)
439
- return plan
440
- except Exception as e:
441
- self.logger.error(f"Failed to parse plan: {str(e)}")
442
- return None
443
-
444
- async def _get_next_step(
445
- self, objective: str, plan_result: PlanResult, request_params: RequestParams
446
- ) -> Optional[PlanningStep]:
447
- """
448
- Generate just the next step for iterative planning.
449
-
450
- Args:
451
- objective: The objective to achieve
452
- plan_result: Current plan execution state
453
- request_params: Request parameters
454
-
455
- Returns:
456
- Next step to execute, or None if parsing fails
457
- """
458
- # Format agent information
459
- agents = "\n".join(
460
- [self._format_agent_info(agent_name) for agent_name in self.agents.keys()]
461
- )
462
-
463
- # Determine plan status
464
- if plan_result.is_complete:
465
- plan_status = "Plan Status: Complete"
466
- elif plan_result.step_results:
467
- plan_status = "Plan Status: In Progress"
468
- else:
469
- plan_status = "Plan Status: Not Started"
470
-
471
- # Calculate iteration information
472
- max_iterations = request_params.max_iterations
473
- current_iteration = len(plan_result.step_results)
474
- iterations_remaining = max_iterations - current_iteration
475
- iterations_info = (
476
- f"Planning Budget: {iterations_remaining} of {max_iterations} iterations remaining"
477
- )
478
-
479
- # Format the planning prompt
480
- prompt = ITERATIVE_PLAN_PROMPT_TEMPLATE.format(
481
- objective=objective,
482
- plan_result=format_plan_result(plan_result),
483
- plan_status=plan_status,
484
- iterations_info=iterations_info,
485
- agents=agents,
486
- )
487
-
488
- # Get structured response from LLM
489
- try:
490
- plan_msg = PromptMessageMultipart(
491
- role="user", content=[TextContent(type="text", text=prompt)]
492
- )
493
- next_step, _ = await self._llm.structured([plan_msg], PlanningStep, request_params)
494
- return next_step
495
- except Exception as e:
496
- self.logger.error(f"Failed to parse next step: {str(e)}")
497
- return None
498
-
499
- def _validate_agent_names(self, plan: Plan) -> None:
500
- """
501
- Validate all agent names in a plan before execution.
502
-
503
- Args:
504
- plan: The plan to validate
505
- """
506
- if plan is None:
507
- self.logger.error("Cannot validate agent names: plan is None")
508
- return
509
-
510
- invalid_agents = []
511
-
512
- for step in plan.steps:
513
- for task in step.tasks:
514
- if task.agent not in self.agents:
515
- invalid_agents.append(task.agent)
516
-
517
- if invalid_agents:
518
- available_agents = ", ".join(self.agents.keys())
519
- invalid_list = ", ".join(invalid_agents)
520
- self.logger.error(
521
- f"Plan contains invalid agent names: {invalid_list}. Available agents: {available_agents}"
522
- )
523
-
524
- def _format_agent_info(self, agent_name: str) -> str:
525
- """
526
- Format agent information for display in prompts.
527
-
528
- Args:
529
- agent_name: Name of the agent to format
530
-
531
- Returns:
532
- Formatted agent information string
533
- """
534
- agent = self.agents.get(agent_name)
535
- if not agent:
536
- self.logger.error(f"Agent '{agent_name}' not found in orchestrator agents")
537
- return ""
538
-
539
- # Get agent instruction or default description
540
- instruction = agent.instruction if agent.instruction else f"Agent '{agent_name}'"
541
-
542
- # Format with XML tags
543
- return f'<fastagent:agent name="{agent_name}">{instruction}</fastagent:agent>'
544
-
545
- async def _planner_generate_str(self, message: str, request_params: RequestParams) -> str:
546
- """
547
- Generate string response from the orchestrator's own LLM.
548
-
549
- Args:
550
- message: Message to send to the LLM
551
- request_params: Request parameters
552
-
553
- Returns:
554
- String response from the LLM
555
- """
556
- # Create prompt message
557
- prompt = PromptMessageMultipart(
558
- role="user", content=[TextContent(type="text", text=message)]
559
- )
560
-
561
- # Get response from LLM
562
- response = await self._llm.generate([prompt], request_params)
563
- return response.all_text()
564
-
565
- def _merge_request_params(self, request_params: Optional[RequestParams]) -> RequestParams:
566
- """
567
- Merge provided request parameters with defaults.
568
-
569
- Args:
570
- request_params: Optional request parameters to merge
571
-
572
- Returns:
573
- Merged request parameters
574
- """
575
- # Create orchestrator-specific defaults
576
- defaults = RequestParams(
577
- use_history=False, # Orchestrator doesn't use history
578
- max_iterations=5, # Default to 5 iterations
579
- maxTokens=8192, # Higher limit for planning
580
- parallel_tool_calls=True,
581
- )
582
-
583
- # If base params provided, merge with defaults
584
- if request_params:
585
- # Create copy of defaults
586
- params = defaults.model_copy()
587
- # Update with provided params
588
- if isinstance(request_params, dict):
589
- params = params.model_copy(update=request_params)
590
- else:
591
- params = params.model_copy(update=request_params.model_dump())
592
-
593
- # Force specific settings
594
- params.use_history = False
595
- return params
596
-
597
- return defaults