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.
- {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/METADATA +3 -4
- fast_agent_mcp-0.2.0.dist-info/RECORD +123 -0
- mcp_agent/__init__.py +75 -0
- mcp_agent/agents/agent.py +61 -415
- mcp_agent/agents/base_agent.py +522 -0
- mcp_agent/agents/workflow/__init__.py +1 -0
- mcp_agent/agents/workflow/chain_agent.py +173 -0
- mcp_agent/agents/workflow/evaluator_optimizer.py +362 -0
- mcp_agent/agents/workflow/orchestrator_agent.py +591 -0
- mcp_agent/{workflows/orchestrator → agents/workflow}/orchestrator_models.py +11 -21
- mcp_agent/agents/workflow/parallel_agent.py +182 -0
- mcp_agent/agents/workflow/router_agent.py +307 -0
- mcp_agent/app.py +15 -19
- mcp_agent/cli/commands/bootstrap.py +19 -38
- mcp_agent/cli/commands/config.py +4 -4
- mcp_agent/cli/commands/setup.py +7 -14
- mcp_agent/cli/main.py +7 -10
- mcp_agent/cli/terminal.py +3 -3
- mcp_agent/config.py +25 -40
- mcp_agent/context.py +12 -21
- mcp_agent/context_dependent.py +3 -5
- mcp_agent/core/agent_types.py +10 -7
- mcp_agent/core/direct_agent_app.py +179 -0
- mcp_agent/core/direct_decorators.py +443 -0
- mcp_agent/core/direct_factory.py +476 -0
- mcp_agent/core/enhanced_prompt.py +23 -55
- mcp_agent/core/exceptions.py +8 -8
- mcp_agent/core/fastagent.py +145 -371
- mcp_agent/core/interactive_prompt.py +424 -0
- mcp_agent/core/mcp_content.py +17 -17
- mcp_agent/core/prompt.py +6 -9
- mcp_agent/core/request_params.py +6 -3
- mcp_agent/core/validation.py +92 -18
- mcp_agent/executor/decorator_registry.py +9 -17
- mcp_agent/executor/executor.py +8 -17
- mcp_agent/executor/task_registry.py +2 -4
- mcp_agent/executor/temporal.py +19 -41
- mcp_agent/executor/workflow.py +3 -5
- mcp_agent/executor/workflow_signal.py +15 -21
- mcp_agent/human_input/handler.py +4 -7
- mcp_agent/human_input/types.py +2 -3
- mcp_agent/llm/__init__.py +2 -0
- mcp_agent/llm/augmented_llm.py +450 -0
- mcp_agent/llm/augmented_llm_passthrough.py +162 -0
- mcp_agent/llm/augmented_llm_playback.py +83 -0
- mcp_agent/llm/memory.py +103 -0
- mcp_agent/{workflows/llm → llm}/model_factory.py +22 -16
- mcp_agent/{workflows/llm → llm}/prompt_utils.py +1 -3
- mcp_agent/llm/providers/__init__.py +8 -0
- mcp_agent/{workflows/llm → llm/providers}/anthropic_utils.py +8 -25
- mcp_agent/{workflows/llm → llm/providers}/augmented_llm_anthropic.py +56 -194
- mcp_agent/llm/providers/augmented_llm_deepseek.py +53 -0
- mcp_agent/{workflows/llm → llm/providers}/augmented_llm_openai.py +99 -190
- mcp_agent/{workflows/llm → llm}/providers/multipart_converter_anthropic.py +72 -71
- mcp_agent/{workflows/llm → llm}/providers/multipart_converter_openai.py +65 -71
- mcp_agent/{workflows/llm → llm}/providers/openai_multipart.py +16 -44
- mcp_agent/{workflows/llm → llm/providers}/openai_utils.py +4 -4
- mcp_agent/{workflows/llm → llm}/providers/sampling_converter_anthropic.py +9 -11
- mcp_agent/{workflows/llm → llm}/providers/sampling_converter_openai.py +8 -12
- mcp_agent/{workflows/llm → llm}/sampling_converter.py +3 -31
- mcp_agent/llm/sampling_format_converter.py +37 -0
- mcp_agent/logging/events.py +1 -5
- mcp_agent/logging/json_serializer.py +7 -6
- mcp_agent/logging/listeners.py +20 -23
- mcp_agent/logging/logger.py +17 -19
- mcp_agent/logging/rich_progress.py +10 -8
- mcp_agent/logging/tracing.py +4 -6
- mcp_agent/logging/transport.py +22 -22
- mcp_agent/mcp/gen_client.py +1 -3
- mcp_agent/mcp/interfaces.py +117 -110
- mcp_agent/mcp/logger_textio.py +97 -0
- mcp_agent/mcp/mcp_agent_client_session.py +7 -7
- mcp_agent/mcp/mcp_agent_server.py +8 -8
- mcp_agent/mcp/mcp_aggregator.py +102 -143
- mcp_agent/mcp/mcp_connection_manager.py +20 -27
- mcp_agent/mcp/prompt_message_multipart.py +68 -16
- mcp_agent/mcp/prompt_render.py +77 -0
- mcp_agent/mcp/prompt_serialization.py +30 -48
- mcp_agent/mcp/prompts/prompt_constants.py +18 -0
- mcp_agent/mcp/prompts/prompt_helpers.py +327 -0
- mcp_agent/mcp/prompts/prompt_load.py +109 -0
- mcp_agent/mcp/prompts/prompt_server.py +155 -195
- mcp_agent/mcp/prompts/prompt_template.py +35 -66
- mcp_agent/mcp/resource_utils.py +7 -14
- mcp_agent/mcp/sampling.py +17 -17
- mcp_agent/mcp_server/agent_server.py +13 -17
- mcp_agent/mcp_server_registry.py +13 -22
- mcp_agent/resources/examples/{workflows → in_dev}/agent_build.py +3 -2
- mcp_agent/resources/examples/in_dev/slides.py +110 -0
- mcp_agent/resources/examples/internal/agent.py +6 -3
- mcp_agent/resources/examples/internal/fastagent.config.yaml +8 -2
- mcp_agent/resources/examples/internal/job.py +2 -1
- mcp_agent/resources/examples/internal/prompt_category.py +1 -1
- mcp_agent/resources/examples/internal/prompt_sizing.py +3 -5
- mcp_agent/resources/examples/internal/sizer.py +2 -1
- mcp_agent/resources/examples/internal/social.py +2 -1
- mcp_agent/resources/examples/prompting/agent.py +2 -1
- mcp_agent/resources/examples/prompting/image_server.py +4 -8
- mcp_agent/resources/examples/prompting/work_with_image.py +19 -0
- mcp_agent/ui/console_display.py +16 -20
- fast_agent_mcp-0.1.12.dist-info/RECORD +0 -161
- mcp_agent/core/agent_app.py +0 -646
- mcp_agent/core/agent_utils.py +0 -71
- mcp_agent/core/decorators.py +0 -455
- mcp_agent/core/factory.py +0 -463
- mcp_agent/core/proxies.py +0 -269
- mcp_agent/core/types.py +0 -24
- mcp_agent/eval/__init__.py +0 -0
- mcp_agent/mcp/stdio.py +0 -111
- mcp_agent/resources/examples/data-analysis/analysis-campaign.py +0 -188
- mcp_agent/resources/examples/data-analysis/analysis.py +0 -65
- mcp_agent/resources/examples/data-analysis/fastagent.config.yaml +0 -41
- mcp_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +0 -1471
- mcp_agent/resources/examples/mcp_researcher/researcher-eval.py +0 -53
- mcp_agent/resources/examples/researcher/fastagent.config.yaml +0 -66
- mcp_agent/resources/examples/researcher/researcher-eval.py +0 -53
- mcp_agent/resources/examples/researcher/researcher-imp.py +0 -190
- mcp_agent/resources/examples/researcher/researcher.py +0 -38
- mcp_agent/resources/examples/workflows/chaining.py +0 -44
- mcp_agent/resources/examples/workflows/evaluator.py +0 -78
- mcp_agent/resources/examples/workflows/fastagent.config.yaml +0 -24
- mcp_agent/resources/examples/workflows/human_input.py +0 -25
- mcp_agent/resources/examples/workflows/orchestrator.py +0 -73
- mcp_agent/resources/examples/workflows/parallel.py +0 -78
- mcp_agent/resources/examples/workflows/router.py +0 -53
- mcp_agent/resources/examples/workflows/sse.py +0 -23
- mcp_agent/telemetry/__init__.py +0 -0
- mcp_agent/telemetry/usage_tracking.py +0 -18
- mcp_agent/workflows/__init__.py +0 -0
- mcp_agent/workflows/embedding/__init__.py +0 -0
- mcp_agent/workflows/embedding/embedding_base.py +0 -61
- mcp_agent/workflows/embedding/embedding_cohere.py +0 -49
- mcp_agent/workflows/embedding/embedding_openai.py +0 -46
- mcp_agent/workflows/evaluator_optimizer/__init__.py +0 -0
- mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +0 -481
- mcp_agent/workflows/intent_classifier/__init__.py +0 -0
- mcp_agent/workflows/intent_classifier/intent_classifier_base.py +0 -120
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding.py +0 -134
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding_cohere.py +0 -45
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding_openai.py +0 -45
- mcp_agent/workflows/intent_classifier/intent_classifier_llm.py +0 -161
- mcp_agent/workflows/intent_classifier/intent_classifier_llm_anthropic.py +0 -60
- mcp_agent/workflows/intent_classifier/intent_classifier_llm_openai.py +0 -60
- mcp_agent/workflows/llm/__init__.py +0 -0
- mcp_agent/workflows/llm/augmented_llm.py +0 -753
- mcp_agent/workflows/llm/augmented_llm_passthrough.py +0 -241
- mcp_agent/workflows/llm/augmented_llm_playback.py +0 -109
- mcp_agent/workflows/llm/providers/__init__.py +0 -8
- mcp_agent/workflows/llm/sampling_format_converter.py +0 -22
- mcp_agent/workflows/orchestrator/__init__.py +0 -0
- mcp_agent/workflows/orchestrator/orchestrator.py +0 -578
- mcp_agent/workflows/parallel/__init__.py +0 -0
- mcp_agent/workflows/parallel/fan_in.py +0 -350
- mcp_agent/workflows/parallel/fan_out.py +0 -187
- mcp_agent/workflows/parallel/parallel_llm.py +0 -166
- mcp_agent/workflows/router/__init__.py +0 -0
- mcp_agent/workflows/router/router_base.py +0 -368
- mcp_agent/workflows/router/router_embedding.py +0 -240
- mcp_agent/workflows/router/router_embedding_cohere.py +0 -59
- mcp_agent/workflows/router/router_embedding_openai.py +0 -59
- mcp_agent/workflows/router/router_llm.py +0 -320
- mcp_agent/workflows/swarm/__init__.py +0 -0
- mcp_agent/workflows/swarm/swarm.py +0 -320
- mcp_agent/workflows/swarm/swarm_anthropic.py +0 -42
- mcp_agent/workflows/swarm/swarm_openai.py +0 -41
- {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/licenses/LICENSE +0 -0
- /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" #
|
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
|
-
|
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
|
-
#
|
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
|