fast-agent-mcp 0.1.12__py3-none-any.whl → 0.2.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.
Files changed (169) hide show
  1. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/METADATA +3 -4
  2. fast_agent_mcp-0.2.0.dist-info/RECORD +123 -0
  3. mcp_agent/__init__.py +75 -0
  4. mcp_agent/agents/agent.py +61 -415
  5. mcp_agent/agents/base_agent.py +522 -0
  6. mcp_agent/agents/workflow/__init__.py +1 -0
  7. mcp_agent/agents/workflow/chain_agent.py +173 -0
  8. mcp_agent/agents/workflow/evaluator_optimizer.py +362 -0
  9. mcp_agent/agents/workflow/orchestrator_agent.py +591 -0
  10. mcp_agent/{workflows/orchestrator → agents/workflow}/orchestrator_models.py +11 -21
  11. mcp_agent/agents/workflow/parallel_agent.py +182 -0
  12. mcp_agent/agents/workflow/router_agent.py +307 -0
  13. mcp_agent/app.py +15 -19
  14. mcp_agent/cli/commands/bootstrap.py +19 -38
  15. mcp_agent/cli/commands/config.py +4 -4
  16. mcp_agent/cli/commands/setup.py +7 -14
  17. mcp_agent/cli/main.py +7 -10
  18. mcp_agent/cli/terminal.py +3 -3
  19. mcp_agent/config.py +25 -40
  20. mcp_agent/context.py +12 -21
  21. mcp_agent/context_dependent.py +3 -5
  22. mcp_agent/core/agent_types.py +10 -7
  23. mcp_agent/core/direct_agent_app.py +179 -0
  24. mcp_agent/core/direct_decorators.py +443 -0
  25. mcp_agent/core/direct_factory.py +476 -0
  26. mcp_agent/core/enhanced_prompt.py +23 -55
  27. mcp_agent/core/exceptions.py +8 -8
  28. mcp_agent/core/fastagent.py +145 -371
  29. mcp_agent/core/interactive_prompt.py +424 -0
  30. mcp_agent/core/mcp_content.py +17 -17
  31. mcp_agent/core/prompt.py +6 -9
  32. mcp_agent/core/request_params.py +6 -3
  33. mcp_agent/core/validation.py +92 -18
  34. mcp_agent/executor/decorator_registry.py +9 -17
  35. mcp_agent/executor/executor.py +8 -17
  36. mcp_agent/executor/task_registry.py +2 -4
  37. mcp_agent/executor/temporal.py +19 -41
  38. mcp_agent/executor/workflow.py +3 -5
  39. mcp_agent/executor/workflow_signal.py +15 -21
  40. mcp_agent/human_input/handler.py +4 -7
  41. mcp_agent/human_input/types.py +2 -3
  42. mcp_agent/llm/__init__.py +2 -0
  43. mcp_agent/llm/augmented_llm.py +450 -0
  44. mcp_agent/llm/augmented_llm_passthrough.py +162 -0
  45. mcp_agent/llm/augmented_llm_playback.py +83 -0
  46. mcp_agent/llm/memory.py +103 -0
  47. mcp_agent/{workflows/llm → llm}/model_factory.py +22 -16
  48. mcp_agent/{workflows/llm → llm}/prompt_utils.py +1 -3
  49. mcp_agent/llm/providers/__init__.py +8 -0
  50. mcp_agent/{workflows/llm → llm/providers}/anthropic_utils.py +8 -25
  51. mcp_agent/{workflows/llm → llm/providers}/augmented_llm_anthropic.py +56 -194
  52. mcp_agent/llm/providers/augmented_llm_deepseek.py +53 -0
  53. mcp_agent/{workflows/llm → llm/providers}/augmented_llm_openai.py +99 -190
  54. mcp_agent/{workflows/llm → llm}/providers/multipart_converter_anthropic.py +72 -71
  55. mcp_agent/{workflows/llm → llm}/providers/multipart_converter_openai.py +65 -71
  56. mcp_agent/{workflows/llm → llm}/providers/openai_multipart.py +16 -44
  57. mcp_agent/{workflows/llm → llm/providers}/openai_utils.py +4 -4
  58. mcp_agent/{workflows/llm → llm}/providers/sampling_converter_anthropic.py +9 -11
  59. mcp_agent/{workflows/llm → llm}/providers/sampling_converter_openai.py +8 -12
  60. mcp_agent/{workflows/llm → llm}/sampling_converter.py +3 -31
  61. mcp_agent/llm/sampling_format_converter.py +37 -0
  62. mcp_agent/logging/events.py +1 -5
  63. mcp_agent/logging/json_serializer.py +7 -6
  64. mcp_agent/logging/listeners.py +20 -23
  65. mcp_agent/logging/logger.py +17 -19
  66. mcp_agent/logging/rich_progress.py +10 -8
  67. mcp_agent/logging/tracing.py +4 -6
  68. mcp_agent/logging/transport.py +22 -22
  69. mcp_agent/mcp/gen_client.py +1 -3
  70. mcp_agent/mcp/interfaces.py +117 -110
  71. mcp_agent/mcp/logger_textio.py +97 -0
  72. mcp_agent/mcp/mcp_agent_client_session.py +7 -7
  73. mcp_agent/mcp/mcp_agent_server.py +8 -8
  74. mcp_agent/mcp/mcp_aggregator.py +102 -143
  75. mcp_agent/mcp/mcp_connection_manager.py +20 -27
  76. mcp_agent/mcp/prompt_message_multipart.py +68 -16
  77. mcp_agent/mcp/prompt_render.py +77 -0
  78. mcp_agent/mcp/prompt_serialization.py +30 -48
  79. mcp_agent/mcp/prompts/prompt_constants.py +18 -0
  80. mcp_agent/mcp/prompts/prompt_helpers.py +327 -0
  81. mcp_agent/mcp/prompts/prompt_load.py +109 -0
  82. mcp_agent/mcp/prompts/prompt_server.py +155 -195
  83. mcp_agent/mcp/prompts/prompt_template.py +35 -66
  84. mcp_agent/mcp/resource_utils.py +7 -14
  85. mcp_agent/mcp/sampling.py +17 -17
  86. mcp_agent/mcp_server/agent_server.py +13 -17
  87. mcp_agent/mcp_server_registry.py +13 -22
  88. mcp_agent/resources/examples/{workflows → in_dev}/agent_build.py +3 -2
  89. mcp_agent/resources/examples/in_dev/slides.py +110 -0
  90. mcp_agent/resources/examples/internal/agent.py +6 -3
  91. mcp_agent/resources/examples/internal/fastagent.config.yaml +8 -2
  92. mcp_agent/resources/examples/internal/job.py +2 -1
  93. mcp_agent/resources/examples/internal/prompt_category.py +1 -1
  94. mcp_agent/resources/examples/internal/prompt_sizing.py +3 -5
  95. mcp_agent/resources/examples/internal/sizer.py +2 -1
  96. mcp_agent/resources/examples/internal/social.py +2 -1
  97. mcp_agent/resources/examples/prompting/agent.py +2 -1
  98. mcp_agent/resources/examples/prompting/image_server.py +4 -8
  99. mcp_agent/resources/examples/prompting/work_with_image.py +19 -0
  100. mcp_agent/ui/console_display.py +16 -20
  101. fast_agent_mcp-0.1.12.dist-info/RECORD +0 -161
  102. mcp_agent/core/agent_app.py +0 -646
  103. mcp_agent/core/agent_utils.py +0 -71
  104. mcp_agent/core/decorators.py +0 -455
  105. mcp_agent/core/factory.py +0 -463
  106. mcp_agent/core/proxies.py +0 -269
  107. mcp_agent/core/types.py +0 -24
  108. mcp_agent/eval/__init__.py +0 -0
  109. mcp_agent/mcp/stdio.py +0 -111
  110. mcp_agent/resources/examples/data-analysis/analysis-campaign.py +0 -188
  111. mcp_agent/resources/examples/data-analysis/analysis.py +0 -65
  112. mcp_agent/resources/examples/data-analysis/fastagent.config.yaml +0 -41
  113. mcp_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +0 -1471
  114. mcp_agent/resources/examples/mcp_researcher/researcher-eval.py +0 -53
  115. mcp_agent/resources/examples/researcher/fastagent.config.yaml +0 -66
  116. mcp_agent/resources/examples/researcher/researcher-eval.py +0 -53
  117. mcp_agent/resources/examples/researcher/researcher-imp.py +0 -190
  118. mcp_agent/resources/examples/researcher/researcher.py +0 -38
  119. mcp_agent/resources/examples/workflows/chaining.py +0 -44
  120. mcp_agent/resources/examples/workflows/evaluator.py +0 -78
  121. mcp_agent/resources/examples/workflows/fastagent.config.yaml +0 -24
  122. mcp_agent/resources/examples/workflows/human_input.py +0 -25
  123. mcp_agent/resources/examples/workflows/orchestrator.py +0 -73
  124. mcp_agent/resources/examples/workflows/parallel.py +0 -78
  125. mcp_agent/resources/examples/workflows/router.py +0 -53
  126. mcp_agent/resources/examples/workflows/sse.py +0 -23
  127. mcp_agent/telemetry/__init__.py +0 -0
  128. mcp_agent/telemetry/usage_tracking.py +0 -18
  129. mcp_agent/workflows/__init__.py +0 -0
  130. mcp_agent/workflows/embedding/__init__.py +0 -0
  131. mcp_agent/workflows/embedding/embedding_base.py +0 -61
  132. mcp_agent/workflows/embedding/embedding_cohere.py +0 -49
  133. mcp_agent/workflows/embedding/embedding_openai.py +0 -46
  134. mcp_agent/workflows/evaluator_optimizer/__init__.py +0 -0
  135. mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +0 -481
  136. mcp_agent/workflows/intent_classifier/__init__.py +0 -0
  137. mcp_agent/workflows/intent_classifier/intent_classifier_base.py +0 -120
  138. mcp_agent/workflows/intent_classifier/intent_classifier_embedding.py +0 -134
  139. mcp_agent/workflows/intent_classifier/intent_classifier_embedding_cohere.py +0 -45
  140. mcp_agent/workflows/intent_classifier/intent_classifier_embedding_openai.py +0 -45
  141. mcp_agent/workflows/intent_classifier/intent_classifier_llm.py +0 -161
  142. mcp_agent/workflows/intent_classifier/intent_classifier_llm_anthropic.py +0 -60
  143. mcp_agent/workflows/intent_classifier/intent_classifier_llm_openai.py +0 -60
  144. mcp_agent/workflows/llm/__init__.py +0 -0
  145. mcp_agent/workflows/llm/augmented_llm.py +0 -753
  146. mcp_agent/workflows/llm/augmented_llm_passthrough.py +0 -241
  147. mcp_agent/workflows/llm/augmented_llm_playback.py +0 -109
  148. mcp_agent/workflows/llm/providers/__init__.py +0 -8
  149. mcp_agent/workflows/llm/sampling_format_converter.py +0 -22
  150. mcp_agent/workflows/orchestrator/__init__.py +0 -0
  151. mcp_agent/workflows/orchestrator/orchestrator.py +0 -578
  152. mcp_agent/workflows/parallel/__init__.py +0 -0
  153. mcp_agent/workflows/parallel/fan_in.py +0 -350
  154. mcp_agent/workflows/parallel/fan_out.py +0 -187
  155. mcp_agent/workflows/parallel/parallel_llm.py +0 -166
  156. mcp_agent/workflows/router/__init__.py +0 -0
  157. mcp_agent/workflows/router/router_base.py +0 -368
  158. mcp_agent/workflows/router/router_embedding.py +0 -240
  159. mcp_agent/workflows/router/router_embedding_cohere.py +0 -59
  160. mcp_agent/workflows/router/router_embedding_openai.py +0 -59
  161. mcp_agent/workflows/router/router_llm.py +0 -320
  162. mcp_agent/workflows/swarm/__init__.py +0 -0
  163. mcp_agent/workflows/swarm/swarm.py +0 -320
  164. mcp_agent/workflows/swarm/swarm_anthropic.py +0 -42
  165. mcp_agent/workflows/swarm/swarm_openai.py +0 -41
  166. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/WHEEL +0 -0
  167. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/entry_points.txt +0 -0
  168. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/licenses/LICENSE +0 -0
  169. /mcp_agent/{workflows/orchestrator → agents/workflow}/orchestrator_prompts.py +0 -0
@@ -0,0 +1,476 @@
1
+ """
2
+ Direct factory functions for creating agent and workflow instances without proxies.
3
+ Implements type-safe factories with improved error handling.
4
+ """
5
+
6
+ from typing import Any, Callable, Dict, Optional, Protocol, TypeVar
7
+
8
+ from mcp_agent.agents.agent import Agent, AgentConfig
9
+ from mcp_agent.agents.workflow.evaluator_optimizer import (
10
+ EvaluatorOptimizerAgent,
11
+ QualityRating,
12
+ )
13
+ from mcp_agent.agents.workflow.orchestrator_agent import OrchestratorAgent
14
+ from mcp_agent.agents.workflow.parallel_agent import ParallelAgent
15
+ from mcp_agent.agents.workflow.router_agent import RouterAgent
16
+ from mcp_agent.app import MCPApp
17
+ from mcp_agent.core.agent_types import AgentType
18
+ from mcp_agent.core.exceptions import AgentConfigError
19
+ from mcp_agent.core.validation import get_dependencies_groups
20
+ from mcp_agent.event_progress import ProgressAction
21
+ from mcp_agent.llm.augmented_llm import RequestParams
22
+ from mcp_agent.llm.model_factory import ModelFactory
23
+ from mcp_agent.logging.logger import get_logger
24
+
25
+ # Type aliases for improved readability and IDE support
26
+ AgentDict = Dict[str, Agent]
27
+ AgentConfigDict = Dict[str, Dict[str, Any]]
28
+ T = TypeVar("T") # For generic types
29
+
30
+ # Type for model factory functions
31
+ ModelFactoryFn = Callable[[Optional[str], Optional[RequestParams]], Callable[[], Any]]
32
+
33
+
34
+ logger = get_logger(__name__)
35
+
36
+
37
+ class AgentCreatorProtocol(Protocol):
38
+ """Protocol for agent creator functions."""
39
+
40
+ async def __call__(
41
+ self,
42
+ app_instance: MCPApp,
43
+ agents_dict: AgentConfigDict,
44
+ agent_type: AgentType,
45
+ active_agents: Optional[AgentDict] = None,
46
+ model_factory_func: Optional[ModelFactoryFn] = None,
47
+ **kwargs: Any,
48
+ ) -> AgentDict: ...
49
+
50
+
51
+ def get_model_factory(
52
+ context,
53
+ model: Optional[str] = None,
54
+ request_params: Optional[RequestParams] = None,
55
+ default_model: Optional[str] = None,
56
+ cli_model: Optional[str] = None,
57
+ ) -> Callable:
58
+ """
59
+ Get model factory using specified or default model.
60
+ Model string is parsed by ModelFactory to determine provider and reasoning effort.
61
+
62
+ Args:
63
+ context: Application context
64
+ model: Optional model specification string (highest precedence)
65
+ request_params: Optional RequestParams to configure LLM behavior
66
+ default_model: Default model from configuration
67
+ cli_model: Model specified via command line
68
+
69
+ Returns:
70
+ ModelFactory instance for the specified or default model
71
+ """
72
+ # Config has lowest precedence
73
+ model_spec = default_model or context.config.default_model
74
+
75
+ # Command line override has next precedence
76
+ if cli_model:
77
+ model_spec = cli_model
78
+
79
+ # Model from decorator has highest precedence
80
+ if model:
81
+ model_spec = model
82
+
83
+ # Update or create request_params with the final model choice
84
+ if request_params:
85
+ request_params = request_params.model_copy(update={"model": model_spec})
86
+ else:
87
+ request_params = RequestParams(model=model_spec)
88
+
89
+ # Let model factory handle the model string parsing and setup
90
+ return ModelFactory.create_factory(model_spec, request_params=request_params)
91
+
92
+
93
+ async def create_agents_by_type(
94
+ app_instance: MCPApp,
95
+ agents_dict: AgentConfigDict,
96
+ agent_type: AgentType,
97
+ active_agents: Optional[AgentDict] = None,
98
+ model_factory_func: Optional[ModelFactoryFn] = None,
99
+ **kwargs: Any,
100
+ ) -> AgentDict:
101
+ """
102
+ Generic method to create agents of a specific type without using proxies.
103
+
104
+ Args:
105
+ app_instance: The main application instance
106
+ agents_dict: Dictionary of agent configurations
107
+ agent_type: Type of agents to create
108
+ active_agents: Dictionary of already created agents (for dependencies)
109
+ model_factory_func: Function for creating model factories
110
+ **kwargs: Additional type-specific parameters
111
+
112
+ Returns:
113
+ Dictionary of initialized agent instances
114
+ """
115
+ if active_agents is None:
116
+ active_agents = {}
117
+
118
+ if model_factory_func is None:
119
+ # Default factory that just returns the inputs - should be overridden
120
+ def model_factory_func(model=None, request_params=None):
121
+ return lambda: None
122
+
123
+ # Create a dictionary to store the initialized agents
124
+ result_agents: AgentDict = {}
125
+
126
+ # Get all agents of the specified type
127
+ for name, agent_data in agents_dict.items():
128
+ logger.info(
129
+ f"Loaded {name}",
130
+ data={
131
+ "progress_action": ProgressAction.LOADED,
132
+ "agent_name": name,
133
+ },
134
+ )
135
+
136
+ if agent_data["type"] == agent_type.value:
137
+ # Get common configuration
138
+ config = agent_data["config"]
139
+
140
+ # Type-specific initialization
141
+ if agent_type == AgentType.BASIC:
142
+ # Create a basic agent
143
+ agent = Agent(
144
+ config=config,
145
+ context=app_instance.context,
146
+ )
147
+ await agent.initialize()
148
+
149
+ # Attach LLM to the agent
150
+ llm_factory = model_factory_func(
151
+ model=config.model,
152
+ request_params=config.default_request_params,
153
+ )
154
+ await agent.attach_llm(llm_factory)
155
+ result_agents[name] = agent
156
+
157
+ elif agent_type == AgentType.ORCHESTRATOR:
158
+ # Get base params configured with model settings
159
+ base_params = (
160
+ config.default_request_params.model_copy()
161
+ if config.default_request_params
162
+ else RequestParams()
163
+ )
164
+ base_params.use_history = False # Force no history for orchestrator
165
+
166
+ # Get the child agents
167
+ child_agents = []
168
+ for agent_name in agent_data["child_agents"]:
169
+ if agent_name not in active_agents:
170
+ raise AgentConfigError(f"Agent {agent_name} not found")
171
+ agent = active_agents[agent_name]
172
+ child_agents.append(agent)
173
+
174
+ # Create the orchestrator
175
+ orchestrator = OrchestratorAgent(
176
+ config=config,
177
+ context=app_instance.context,
178
+ agents=child_agents,
179
+ plan_type=agent_data.get("plan_type", "full"),
180
+ )
181
+
182
+ # Initialize the orchestrator
183
+ await orchestrator.initialize()
184
+
185
+ # Attach LLM to the orchestrator
186
+ llm_factory = model_factory_func(
187
+ model=config.model,
188
+ request_params=config.default_request_params,
189
+ )
190
+ await orchestrator.attach_llm(llm_factory)
191
+
192
+ result_agents[name] = orchestrator
193
+
194
+ elif agent_type == AgentType.PARALLEL:
195
+ # Get the fan-out and fan-in agents
196
+ fan_in_name = agent_data.get("fan_in")
197
+ fan_out_names = agent_data["fan_out"]
198
+
199
+ # Create or retrieve the fan-in agent
200
+ if not fan_in_name:
201
+ # Create default fan-in agent with auto-generated name
202
+ fan_in_name = f"{name}_fan_in"
203
+ fan_in_agent = await _create_default_fan_in_agent(
204
+ fan_in_name,
205
+ app_instance.context,
206
+ model_factory_func
207
+ )
208
+ # Add to result_agents so it's registered properly
209
+ result_agents[fan_in_name] = fan_in_agent
210
+ elif fan_in_name not in active_agents:
211
+ raise AgentConfigError(f"Fan-in agent {fan_in_name} not found")
212
+ else:
213
+ fan_in_agent = active_agents[fan_in_name]
214
+
215
+ # Get the fan-out agents
216
+ fan_out_agents = []
217
+ for agent_name in fan_out_names:
218
+ if agent_name not in active_agents:
219
+ raise AgentConfigError(f"Fan-out agent {agent_name} not found")
220
+ fan_out_agents.append(active_agents[agent_name])
221
+
222
+ # Create the parallel agent
223
+ parallel = ParallelAgent(
224
+ config=config,
225
+ context=app_instance.context,
226
+ fan_in_agent=fan_in_agent,
227
+ fan_out_agents=fan_out_agents,
228
+ )
229
+ await parallel.initialize()
230
+ result_agents[name] = parallel
231
+
232
+ elif agent_type == AgentType.ROUTER:
233
+ # Get the router agents
234
+ router_agents = []
235
+ for agent_name in agent_data["router_agents"]:
236
+ if agent_name not in active_agents:
237
+ raise AgentConfigError(f"Router agent {agent_name} not found")
238
+ router_agents.append(active_agents[agent_name])
239
+
240
+ # Create the router agent
241
+ router = RouterAgent(
242
+ config=config,
243
+ context=app_instance.context,
244
+ agents=router_agents,
245
+ routing_instruction=agent_data.get("routing_instruction"),
246
+ )
247
+ await router.initialize()
248
+
249
+ # Attach LLM to the router
250
+ llm_factory = model_factory_func(
251
+ model=config.model,
252
+ request_params=config.default_request_params,
253
+ )
254
+ await router.attach_llm(llm_factory)
255
+ result_agents[name] = router
256
+
257
+ elif agent_type == AgentType.CHAIN:
258
+ # Get the chained agents
259
+ chain_agents = []
260
+
261
+ agent_names = agent_data["sequence"]
262
+ if 0 == len(agent_names):
263
+ raise AgentConfigError("No agents in the chain")
264
+
265
+ for agent_name in agent_data["sequence"]:
266
+ if agent_name not in active_agents:
267
+ raise AgentConfigError(f"Chain agent {agent_name} not found")
268
+ chain_agents.append(active_agents[agent_name])
269
+
270
+ from mcp_agent.agents.workflow.chain_agent import ChainAgent
271
+
272
+ # Get the cumulative parameter
273
+ cumulative = agent_data.get("cumulative", False)
274
+
275
+ chain = ChainAgent(
276
+ config=config,
277
+ context=app_instance.context,
278
+ agents=chain_agents,
279
+ cumulative=cumulative,
280
+ )
281
+ await chain.initialize()
282
+ result_agents[name] = chain
283
+
284
+ elif agent_type == AgentType.EVALUATOR_OPTIMIZER:
285
+ # Get the generator and evaluator agents
286
+ generator_name = agent_data["generator"]
287
+ evaluator_name = agent_data["evaluator"]
288
+
289
+ if generator_name not in active_agents:
290
+ raise AgentConfigError(f"Generator agent {generator_name} not found")
291
+
292
+ if evaluator_name not in active_agents:
293
+ raise AgentConfigError(f"Evaluator agent {evaluator_name} not found")
294
+
295
+ generator_agent = active_agents[generator_name]
296
+ evaluator_agent = active_agents[evaluator_name]
297
+
298
+ # Get min_rating and max_refinements from agent_data
299
+ min_rating_str = agent_data.get("min_rating", "GOOD")
300
+ min_rating = QualityRating(min_rating_str)
301
+ max_refinements = agent_data.get("max_refinements", 3)
302
+
303
+ # Create the evaluator-optimizer agent
304
+ evaluator_optimizer = EvaluatorOptimizerAgent(
305
+ config=config,
306
+ context=app_instance.context,
307
+ generator_agent=generator_agent,
308
+ evaluator_agent=evaluator_agent,
309
+ min_rating=min_rating,
310
+ max_refinements=max_refinements,
311
+ )
312
+
313
+ # Initialize the agent
314
+ await evaluator_optimizer.initialize()
315
+ result_agents[name] = evaluator_optimizer
316
+
317
+ else:
318
+ raise ValueError(f"Unknown agent type: {agent_type}")
319
+
320
+ return result_agents
321
+
322
+
323
+ async def create_agents_in_dependency_order(
324
+ app_instance: MCPApp,
325
+ agents_dict: AgentConfigDict,
326
+ model_factory_func: ModelFactoryFn,
327
+ allow_cycles: bool = False,
328
+ ) -> AgentDict:
329
+ """
330
+ Create agent instances in dependency order without proxies.
331
+
332
+ Args:
333
+ app_instance: The main application instance
334
+ agents_dict: Dictionary of agent configurations
335
+ model_factory_func: Function for creating model factories
336
+ allow_cycles: Whether to allow cyclic dependencies
337
+
338
+ Returns:
339
+ Dictionary of initialized agent instances
340
+ """
341
+ # Get the dependencies between agents
342
+ dependencies = get_dependencies_groups(agents_dict, allow_cycles)
343
+
344
+ # Create a dictionary to store all active agents/workflows
345
+ active_agents: AgentDict = {}
346
+
347
+ # Create agent proxies for each group in dependency order
348
+ for group in dependencies:
349
+ # Create basic agents first
350
+ if AgentType.BASIC.value in [agents_dict[name]["type"] for name in group]:
351
+ basic_agents = await create_agents_by_type(
352
+ app_instance,
353
+ {
354
+ name: agents_dict[name]
355
+ for name in group
356
+ if agents_dict[name]["type"] == AgentType.BASIC.value
357
+ },
358
+ AgentType.BASIC,
359
+ active_agents,
360
+ model_factory_func,
361
+ )
362
+ active_agents.update(basic_agents)
363
+
364
+ # Create parallel agents
365
+ if AgentType.PARALLEL.value in [agents_dict[name]["type"] for name in group]:
366
+ parallel_agents = await create_agents_by_type(
367
+ app_instance,
368
+ {
369
+ name: agents_dict[name]
370
+ for name in group
371
+ if agents_dict[name]["type"] == AgentType.PARALLEL.value
372
+ },
373
+ AgentType.PARALLEL,
374
+ active_agents,
375
+ model_factory_func,
376
+ )
377
+ active_agents.update(parallel_agents)
378
+
379
+ # Create router agents
380
+ if AgentType.ROUTER.value in [agents_dict[name]["type"] for name in group]:
381
+ router_agents = await create_agents_by_type(
382
+ app_instance,
383
+ {
384
+ name: agents_dict[name]
385
+ for name in group
386
+ if agents_dict[name]["type"] == AgentType.ROUTER.value
387
+ },
388
+ AgentType.ROUTER,
389
+ active_agents,
390
+ model_factory_func,
391
+ )
392
+ active_agents.update(router_agents)
393
+
394
+ # Create chain agents
395
+ if AgentType.CHAIN.value in [agents_dict[name]["type"] for name in group]:
396
+ chain_agents = await create_agents_by_type(
397
+ app_instance,
398
+ {
399
+ name: agents_dict[name]
400
+ for name in group
401
+ if agents_dict[name]["type"] == AgentType.CHAIN.value
402
+ },
403
+ AgentType.CHAIN,
404
+ active_agents,
405
+ model_factory_func,
406
+ )
407
+ active_agents.update(chain_agents)
408
+
409
+ # Create evaluator-optimizer agents
410
+ if AgentType.EVALUATOR_OPTIMIZER.value in [agents_dict[name]["type"] for name in group]:
411
+ evaluator_agents = await create_agents_by_type(
412
+ app_instance,
413
+ {
414
+ name: agents_dict[name]
415
+ for name in group
416
+ if agents_dict[name]["type"] == AgentType.EVALUATOR_OPTIMIZER.value
417
+ },
418
+ AgentType.EVALUATOR_OPTIMIZER,
419
+ active_agents,
420
+ model_factory_func,
421
+ )
422
+ active_agents.update(evaluator_agents)
423
+
424
+ # Create orchestrator agents last since they might depend on other agents
425
+ if AgentType.ORCHESTRATOR.value in [agents_dict[name]["type"] for name in group]:
426
+ orchestrator_agents = await create_agents_by_type(
427
+ app_instance,
428
+ {
429
+ name: agents_dict[name]
430
+ for name in group
431
+ if agents_dict[name]["type"] == AgentType.ORCHESTRATOR.value
432
+ },
433
+ AgentType.ORCHESTRATOR,
434
+ active_agents,
435
+ model_factory_func,
436
+ )
437
+ active_agents.update(orchestrator_agents)
438
+
439
+ return active_agents
440
+
441
+
442
+ async def _create_default_fan_in_agent(
443
+ fan_in_name: str,
444
+ context,
445
+ model_factory_func: ModelFactoryFn,
446
+ ) -> Agent:
447
+ """
448
+ Create a default fan-in agent for parallel workflows when none is specified.
449
+
450
+ Args:
451
+ fan_in_name: Name for the new fan-in agent
452
+ context: Application context
453
+ model_factory_func: Function for creating model factories
454
+
455
+ Returns:
456
+ Initialized Agent instance for fan-in operations
457
+ """
458
+ # Create a simple config for the fan-in agent with passthrough model
459
+ default_config = AgentConfig(
460
+ name=fan_in_name,
461
+ model="passthrough",
462
+ instruction="You are a passthrough agent that combines outputs from parallel agents."
463
+ )
464
+
465
+ # Create and initialize the default agent
466
+ fan_in_agent = Agent(
467
+ config=default_config,
468
+ context=context,
469
+ )
470
+ await fan_in_agent.initialize()
471
+
472
+ # Attach LLM to the agent
473
+ llm_factory = model_factory_func(model="passthrough")
474
+ await fan_in_agent.attach_llm(llm_factory)
475
+
476
+ return fan_in_agent
@@ -2,17 +2,16 @@
2
2
  Enhanced prompt functionality with advanced prompt_toolkit features.
3
3
  """
4
4
 
5
- from typing import List, Optional
6
5
  from importlib.metadata import version
6
+ from typing import List, Optional
7
+
7
8
  from prompt_toolkit import PromptSession
9
+ from prompt_toolkit.completion import Completer, Completion, WordCompleter
10
+ from prompt_toolkit.filters import Condition
8
11
  from prompt_toolkit.formatted_text import HTML
9
12
  from prompt_toolkit.history import InMemoryHistory
10
13
  from prompt_toolkit.key_binding import KeyBindings
11
- from prompt_toolkit.completion import Completer, Completion, WordCompleter
12
- from prompt_toolkit.lexers import PygmentsLexer
13
- from prompt_toolkit.filters import Condition
14
14
  from prompt_toolkit.styles import Style
15
- from pygments.lexers.python import PythonLexer
16
15
  from rich import print as rich_print
17
16
 
18
17
  from mcp_agent.core.exceptions import PromptExitError
@@ -45,7 +44,7 @@ class AgentCompleter(Completer):
45
44
  commands: List[str] = None,
46
45
  agent_types: dict = None,
47
46
  is_human_input: bool = False,
48
- ):
47
+ ) -> None:
49
48
  self.agents = agents
50
49
  # Map commands to their descriptions for better completion hints
51
50
  self.commands = {
@@ -61,9 +60,7 @@ class AgentCompleter(Completer):
61
60
  if is_human_input:
62
61
  self.commands.pop("agents")
63
62
  self.commands.pop("prompts") # Remove prompts command in human input mode
64
- self.commands.pop(
65
- "prompt", None
66
- ) # Remove prompt command in human input mode
63
+ self.commands.pop("prompt", None) # Remove prompt command in human input mode
67
64
  self.agent_types = agent_types or {}
68
65
 
69
66
  def get_completions(self, document, complete_event):
@@ -103,23 +100,23 @@ def create_keybindings(on_toggle_multiline=None, app=None):
103
100
  kb = KeyBindings()
104
101
 
105
102
  @kb.add("c-m", filter=Condition(lambda: not in_multiline_mode))
106
- def _(event):
103
+ def _(event) -> None:
107
104
  """Enter: accept input when not in multiline mode."""
108
105
  event.current_buffer.validate_and_handle()
109
106
 
110
107
  @kb.add("c-m", filter=Condition(lambda: in_multiline_mode))
111
- def _(event):
108
+ def _(event) -> None:
112
109
  """Enter: insert newline when in multiline mode."""
113
110
  event.current_buffer.insert_text("\n")
114
111
 
115
112
  # Use c-j (Ctrl+J) as an alternative to represent Ctrl+Enter in multiline mode
116
113
  @kb.add("c-j", filter=Condition(lambda: in_multiline_mode))
117
- def _(event):
114
+ def _(event) -> None:
118
115
  """Ctrl+J (equivalent to Ctrl+Enter): Submit in multiline mode."""
119
116
  event.current_buffer.validate_and_handle()
120
117
 
121
118
  @kb.add("c-t")
122
- def _(event):
119
+ def _(event) -> None:
123
120
  """Ctrl+T: Toggle multiline mode."""
124
121
  global in_multiline_mode
125
122
  in_multiline_mode = not in_multiline_mode
@@ -138,7 +135,7 @@ def create_keybindings(on_toggle_multiline=None, app=None):
138
135
  # The toolbar will show the current mode
139
136
 
140
137
  @kb.add("c-l")
141
- def _(event):
138
+ def _(event) -> None:
142
139
  """Ctrl+L: Clear the input buffer."""
143
140
  event.current_buffer.text = ""
144
141
 
@@ -152,7 +149,6 @@ async def get_enhanced_input(
152
149
  show_stop_hint: bool = False,
153
150
  multiline: bool = False,
154
151
  available_agent_names: List[str] = None,
155
- syntax: str = None,
156
152
  agent_types: dict = None,
157
153
  is_human_input: bool = False,
158
154
  toolbar_color: str = "ansiblue",
@@ -167,7 +163,6 @@ async def get_enhanced_input(
167
163
  show_stop_hint: Whether to show the STOP hint
168
164
  multiline: Start in multiline mode
169
165
  available_agent_names: List of agent names for auto-completion
170
- syntax: Syntax highlighting (e.g., 'python', 'sql')
171
166
  agent_types: Dictionary mapping agent names to their types for display
172
167
  is_human_input: Whether this is a human input request (disables agent selection features)
173
168
  toolbar_color: Color to use for the agent name in the toolbar (default: "ansiblue")
@@ -187,7 +182,7 @@ async def get_enhanced_input(
187
182
  agent_histories[agent_name] = InMemoryHistory()
188
183
 
189
184
  # Define callback for multiline toggle
190
- def on_multiline_toggle(enabled):
185
+ def on_multiline_toggle(enabled) -> None:
191
186
  nonlocal session
192
187
  if hasattr(session, "app") and session.app:
193
188
  session.app.invalidate()
@@ -209,9 +204,7 @@ async def get_enhanced_input(
209
204
  ("↑/↓", "History"),
210
205
  ]
211
206
 
212
- newline = (
213
- "Ctrl+<Enter>:Submit" if in_multiline_mode else "<Enter>:Submit"
214
- )
207
+ newline = "Ctrl+<Enter>:Submit" if in_multiline_mode else "<Enter>:Submit"
215
208
 
216
209
  # Only show relevant shortcuts based on mode
217
210
  shortcuts = [(k, v) for k, v in shortcuts if v]
@@ -241,7 +234,6 @@ async def get_enhanced_input(
241
234
  is_human_input=is_human_input,
242
235
  ),
243
236
  complete_while_typing=True,
244
- lexer=PygmentsLexer(PythonLexer) if syntax == "python" else None,
245
237
  multiline=Condition(lambda: in_multiline_mode),
246
238
  complete_in_thread=True,
247
239
  mouse_support=False,
@@ -250,9 +242,7 @@ async def get_enhanced_input(
250
242
  )
251
243
 
252
244
  # Create key bindings with a reference to the app
253
- bindings = create_keybindings(
254
- on_toggle_multiline=on_multiline_toggle, app=session.app
255
- )
245
+ bindings = create_keybindings(on_toggle_multiline=on_multiline_toggle, app=session.app)
256
246
  session.app.key_bindings = bindings
257
247
 
258
248
  # Create formatted prompt text
@@ -267,16 +257,12 @@ async def get_enhanced_input(
267
257
  if default == "STOP":
268
258
  rich_print("Enter a prompt, [red]STOP[/red] to finish")
269
259
  if default:
270
- rich_print(
271
- f"Press <ENTER> to use the default prompt:\n[cyan]{default}[/cyan]"
272
- )
260
+ rich_print(f"Press <ENTER> to use the default prompt:\n[cyan]{default}[/cyan]")
273
261
 
274
262
  # Mention available features but only on first usage globally
275
263
  if not help_message_shown:
276
264
  if is_human_input:
277
- rich_print(
278
- "[dim]Type /help for commands. Ctrl+T toggles multiline mode.[/dim]"
279
- )
265
+ rich_print("[dim]Type /help for commands. Ctrl+T toggles multiline mode.[/dim]")
280
266
  else:
281
267
  rich_print(
282
268
  "[dim]Type /help for commands, @agent to switch agent. Ctrl+T toggles multiline mode.[/dim]"
@@ -299,7 +285,7 @@ async def get_enhanced_input(
299
285
  elif cmd == "agents":
300
286
  return "LIST_AGENTS"
301
287
  elif cmd == "prompts":
302
- return "SELECT_PROMPT" # Changed from LIST_PROMPTS to directly launch selection UI
288
+ return "SELECT_PROMPT" # Directly launch prompt selection UI
303
289
  elif cmd == "prompt" and len(cmd_parts) > 1:
304
290
  # Direct prompt selection with name
305
291
  return f"SELECT_PROMPT:{cmd_parts[1].strip()}"
@@ -366,9 +352,7 @@ async def get_selection_input(
366
352
 
367
353
  try:
368
354
  # Get user input
369
- selection = await prompt_session.prompt_async(
370
- prompt_text, default=default or ""
371
- )
355
+ selection = await prompt_session.prompt_async(prompt_text, default=default or "")
372
356
 
373
357
  # Handle cancellation
374
358
  if allow_cancel and not selection.strip():
@@ -452,13 +436,9 @@ async def handle_special_commands(command, agent_app=None):
452
436
  rich_print(" /prompt <name> - Apply a specific prompt by name")
453
437
  rich_print(" @agent_name - Switch to agent")
454
438
  rich_print(" STOP - Return control back to the workflow")
455
- rich_print(
456
- " EXIT - Exit fast-agent, terminating any running workflows"
457
- )
439
+ rich_print(" EXIT - Exit fast-agent, terminating any running workflows")
458
440
  rich_print("\n[bold]Keyboard Shortcuts:[/bold]")
459
- rich_print(
460
- " Enter - Submit (normal mode) / New line (multiline mode)"
461
- )
441
+ rich_print(" Enter - Submit (normal mode) / New line (multiline mode)")
462
442
  rich_print(" Ctrl+Enter - Always submit (in any mode)")
463
443
  rich_print(" Ctrl+T - Toggle multiline mode")
464
444
  rich_print(" Ctrl+L - Clear input")
@@ -482,17 +462,7 @@ async def handle_special_commands(command, agent_app=None):
482
462
  rich_print("[yellow]No agents available[/yellow]")
483
463
  return True
484
464
 
485
- elif command == "LIST_PROMPTS":
486
- # Return a dictionary with a list_prompts action to be handled by the caller
487
- # The actual prompt listing is implemented in the AgentApp class
488
- if agent_app:
489
- rich_print("\n[bold]Fetching available MCP prompts...[/bold]")
490
- return {"list_prompts": True}
491
- else:
492
- rich_print(
493
- "[yellow]Prompt listing is not available outside of an agent context[/yellow]"
494
- )
495
- return True
465
+ # Removed LIST_PROMPTS handling as it's now covered by SELECT_PROMPT
496
466
 
497
467
  elif command == "SELECT_PROMPT" or (
498
468
  isinstance(command, str) and command.startswith("SELECT_PROMPT:")
@@ -516,12 +486,10 @@ async def handle_special_commands(command, agent_app=None):
516
486
  agent_name = command.split(":", 1)[1]
517
487
  if agent_name in available_agents:
518
488
  if agent_app:
519
- # rich_print(f"[green]Switching to agent: {agent_name}[/green]")
489
+ # The parameter can be the actual agent_app or just True to enable switching
520
490
  return {"switch_agent": agent_name}
521
491
  else:
522
- rich_print(
523
- "[yellow]Agent switching not available in this context[/yellow]"
524
- )
492
+ rich_print("[yellow]Agent switching not available in this context[/yellow]")
525
493
  else:
526
494
  rich_print(f"[red]Unknown agent: {agent_name}[/red]")
527
495
  return True