fast-agent-mcp 0.0.8__py3-none-any.whl → 0.0.11__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.
- {fast_agent_mcp-0.0.8.dist-info → fast_agent_mcp-0.0.11.dist-info}/METADATA +15 -9
- {fast_agent_mcp-0.0.8.dist-info → fast_agent_mcp-0.0.11.dist-info}/RECORD +28 -26
- mcp_agent/app.py +4 -4
- mcp_agent/cli/commands/bootstrap.py +4 -0
- mcp_agent/cli/commands/setup.py +1 -1
- mcp_agent/core/fastagent.py +498 -369
- mcp_agent/event_progress.py +5 -2
- mcp_agent/human_input/handler.py +6 -2
- mcp_agent/logging/rich_progress.py +10 -5
- mcp_agent/mcp/mcp_aggregator.py +2 -1
- mcp_agent/mcp/mcp_connection_manager.py +67 -37
- mcp_agent/resources/examples/data-analysis/analysis.py +1 -1
- mcp_agent/resources/examples/data-analysis/fastagent.config.yaml +2 -0
- mcp_agent/resources/examples/internal/job.py +83 -0
- mcp_agent/resources/examples/workflows/agent_build.py +61 -0
- mcp_agent/resources/examples/workflows/chaining.py +0 -1
- mcp_agent/resources/examples/workflows/human_input.py +0 -1
- mcp_agent/resources/examples/workflows/orchestrator.py +1 -7
- mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +63 -65
- mcp_agent/workflows/llm/augmented_llm.py +9 -1
- mcp_agent/workflows/llm/augmented_llm_anthropic.py +28 -23
- mcp_agent/workflows/llm/model_factory.py +25 -11
- mcp_agent/workflows/orchestrator/orchestrator.py +106 -100
- mcp_agent/workflows/orchestrator/orchestrator_prompts.py +11 -6
- mcp_agent/workflows/router/router_llm.py +13 -2
- {fast_agent_mcp-0.0.8.dist-info → fast_agent_mcp-0.0.11.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.0.8.dist-info → fast_agent_mcp-0.0.11.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.0.8.dist-info → fast_agent_mcp-0.0.11.dist-info}/licenses/LICENSE +0 -0
mcp_agent/core/fastagent.py
CHANGED
|
@@ -19,6 +19,7 @@ from mcp_agent.core.exceptions import (
|
|
|
19
19
|
from mcp_agent.app import MCPApp
|
|
20
20
|
from mcp_agent.agents.agent import Agent, AgentConfig
|
|
21
21
|
from mcp_agent.context_dependent import ContextDependent
|
|
22
|
+
from mcp_agent.event_progress import ProgressAction
|
|
22
23
|
from mcp_agent.workflows.orchestrator.orchestrator import Orchestrator
|
|
23
24
|
from mcp_agent.workflows.parallel.parallel_llm import ParallelLLM
|
|
24
25
|
from mcp_agent.workflows.evaluator_optimizer.evaluator_optimizer import (
|
|
@@ -31,9 +32,10 @@ from rich.prompt import Prompt
|
|
|
31
32
|
from rich import print
|
|
32
33
|
from mcp_agent.progress_display import progress_display
|
|
33
34
|
from mcp_agent.workflows.llm.model_factory import ModelFactory
|
|
34
|
-
from mcp_agent.workflows.llm.augmented_llm import RequestParams
|
|
35
|
+
from mcp_agent.workflows.llm.augmented_llm import AugmentedLLM, RequestParams
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
# TODO -- resintate once Windows&Python 3.13 platform issues are fixed
|
|
38
|
+
# import readline # noqa: F401
|
|
37
39
|
|
|
38
40
|
# Type aliases for better readability
|
|
39
41
|
WorkflowType: TypeAlias = Union[
|
|
@@ -127,7 +129,9 @@ class RouterProxy(BaseAgentProxy):
|
|
|
127
129
|
top_result = results[0]
|
|
128
130
|
if isinstance(top_result.result, Agent):
|
|
129
131
|
# Agent route - delegate to the agent
|
|
130
|
-
|
|
132
|
+
agent = top_result.result
|
|
133
|
+
|
|
134
|
+
return await agent._llm.generate_str(message)
|
|
131
135
|
elif isinstance(top_result.result, str):
|
|
132
136
|
# Server route - use the router directly
|
|
133
137
|
return "Tool call requested by router - not yet supported"
|
|
@@ -222,6 +226,34 @@ class FastAgent(ContextDependent):
|
|
|
222
226
|
Provides a simplified way to create and manage agents using decorators.
|
|
223
227
|
"""
|
|
224
228
|
|
|
229
|
+
def __init__(self, name: str, config_path: Optional[str] = None):
|
|
230
|
+
"""
|
|
231
|
+
Initialize the decorator interface.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
name: Name of the application
|
|
235
|
+
config_path: Optional path to config file
|
|
236
|
+
"""
|
|
237
|
+
# Initialize ContextDependent
|
|
238
|
+
super().__init__()
|
|
239
|
+
|
|
240
|
+
# Setup command line argument parsing
|
|
241
|
+
parser = argparse.ArgumentParser(description="MCP Agent Application")
|
|
242
|
+
parser.add_argument(
|
|
243
|
+
"--model",
|
|
244
|
+
help="Override the default model for all agents. Precedence is default < config_file < command line < constructor",
|
|
245
|
+
)
|
|
246
|
+
self.args = parser.parse_args()
|
|
247
|
+
|
|
248
|
+
self.name = name
|
|
249
|
+
self.config_path = config_path
|
|
250
|
+
self._load_config()
|
|
251
|
+
self.app = MCPApp(
|
|
252
|
+
name=name,
|
|
253
|
+
settings=Settings(**self.config) if hasattr(self, "config") else None,
|
|
254
|
+
)
|
|
255
|
+
self.agents: Dict[str, Dict[str, Any]] = {}
|
|
256
|
+
|
|
225
257
|
def _create_proxy(
|
|
226
258
|
self, name: str, instance: AgentOrWorkflow, agent_type: str
|
|
227
259
|
) -> BaseAgentProxy:
|
|
@@ -238,6 +270,11 @@ class FastAgent(ContextDependent):
|
|
|
238
270
|
Raises:
|
|
239
271
|
TypeError: If instance type doesn't match expected type for agent_type
|
|
240
272
|
"""
|
|
273
|
+
if agent_type not in [
|
|
274
|
+
AgentType.PARALLEL.value,
|
|
275
|
+
AgentType.EVALUATOR_OPTIMIZER.value,
|
|
276
|
+
]:
|
|
277
|
+
self._log_agent_load(name)
|
|
241
278
|
if agent_type == AgentType.BASIC.value:
|
|
242
279
|
if not isinstance(instance, Agent):
|
|
243
280
|
raise TypeError(
|
|
@@ -271,34 +308,6 @@ class FastAgent(ContextDependent):
|
|
|
271
308
|
else:
|
|
272
309
|
raise ValueError(f"Unknown agent type: {agent_type}")
|
|
273
310
|
|
|
274
|
-
def __init__(self, name: str, config_path: Optional[str] = None):
|
|
275
|
-
"""
|
|
276
|
-
Initialize the decorator interface.
|
|
277
|
-
|
|
278
|
-
Args:
|
|
279
|
-
name: Name of the application
|
|
280
|
-
config_path: Optional path to config file
|
|
281
|
-
"""
|
|
282
|
-
# Initialize ContextDependent
|
|
283
|
-
super().__init__()
|
|
284
|
-
|
|
285
|
-
# Setup command line argument parsing
|
|
286
|
-
parser = argparse.ArgumentParser(description="MCP Agent Application")
|
|
287
|
-
parser.add_argument(
|
|
288
|
-
"--model",
|
|
289
|
-
help="Override the default model for all agents. Precedence is default < config_file < command line < constructor",
|
|
290
|
-
)
|
|
291
|
-
self.args = parser.parse_args()
|
|
292
|
-
|
|
293
|
-
self.name = name
|
|
294
|
-
self.config_path = config_path
|
|
295
|
-
self._load_config()
|
|
296
|
-
self.app = MCPApp(
|
|
297
|
-
name=name,
|
|
298
|
-
settings=Settings(**self.config) if hasattr(self, "config") else None,
|
|
299
|
-
)
|
|
300
|
-
self.agents: Dict[str, Dict[str, Any]] = {}
|
|
301
|
-
|
|
302
311
|
@property
|
|
303
312
|
def context(self):
|
|
304
313
|
"""Access the application context"""
|
|
@@ -334,7 +343,8 @@ class FastAgent(ContextDependent):
|
|
|
334
343
|
def _validate_workflow_references(self) -> None:
|
|
335
344
|
"""
|
|
336
345
|
Validate that all workflow references point to valid agents/workflows.
|
|
337
|
-
|
|
346
|
+
Also validates that referenced agents have required configuration.
|
|
347
|
+
Raises AgentConfigError if any validation fails.
|
|
338
348
|
"""
|
|
339
349
|
available_components = set(self.agents.keys())
|
|
340
350
|
|
|
@@ -358,7 +368,7 @@ class FastAgent(ContextDependent):
|
|
|
358
368
|
)
|
|
359
369
|
|
|
360
370
|
elif agent_type == AgentType.ORCHESTRATOR.value:
|
|
361
|
-
# Check all child agents exist
|
|
371
|
+
# Check all child agents exist and are properly configured
|
|
362
372
|
child_agents = agent_data["child_agents"]
|
|
363
373
|
missing = [a for a in child_agents if a not in available_components]
|
|
364
374
|
if missing:
|
|
@@ -366,6 +376,18 @@ class FastAgent(ContextDependent):
|
|
|
366
376
|
f"Orchestrator '{name}' references non-existent agents: {', '.join(missing)}"
|
|
367
377
|
)
|
|
368
378
|
|
|
379
|
+
# Validate child agents have required LLM configuration
|
|
380
|
+
for agent_name in child_agents:
|
|
381
|
+
child_data = self.agents[agent_name]
|
|
382
|
+
if child_data["type"] == AgentType.BASIC.value:
|
|
383
|
+
# For basic agents, we'll validate LLM config during creation
|
|
384
|
+
continue
|
|
385
|
+
elif not isinstance(child_data["func"], AugmentedLLM):
|
|
386
|
+
raise AgentConfigError(
|
|
387
|
+
f"Agent '{agent_name}' used by orchestrator '{name}' lacks LLM capability",
|
|
388
|
+
"All agents used by orchestrators must be LLM-capable",
|
|
389
|
+
)
|
|
390
|
+
|
|
369
391
|
elif agent_type == AgentType.ROUTER.value:
|
|
370
392
|
# Check all referenced agents exist
|
|
371
393
|
router_agents = agent_data["agents"]
|
|
@@ -426,13 +448,111 @@ class FastAgent(ContextDependent):
|
|
|
426
448
|
# Let model factory handle the model string parsing and setup
|
|
427
449
|
return ModelFactory.create_factory(model_spec, request_params=request_params)
|
|
428
450
|
|
|
451
|
+
def _create_decorator(
|
|
452
|
+
self,
|
|
453
|
+
agent_type: AgentType,
|
|
454
|
+
default_name: str = None,
|
|
455
|
+
default_instruction: str = None,
|
|
456
|
+
default_servers: List[str] = None,
|
|
457
|
+
default_use_history: bool = True,
|
|
458
|
+
wrapper_needed: bool = False,
|
|
459
|
+
**extra_defaults,
|
|
460
|
+
) -> Callable:
|
|
461
|
+
"""
|
|
462
|
+
Factory method for creating agent decorators with common behavior.
|
|
463
|
+
|
|
464
|
+
Args:
|
|
465
|
+
agent_type: Type of agent/workflow to create
|
|
466
|
+
default_name: Default name to use if not provided
|
|
467
|
+
default_instruction: Default instruction to use if not provided
|
|
468
|
+
default_servers: Default servers list to use if not provided
|
|
469
|
+
default_use_history: Default history setting
|
|
470
|
+
wrapper_needed: Whether to wrap the decorated function
|
|
471
|
+
**extra_defaults: Additional agent/workflow-specific parameters
|
|
472
|
+
"""
|
|
473
|
+
|
|
474
|
+
def decorator_wrapper(**kwargs):
|
|
475
|
+
# Apply defaults for common parameters
|
|
476
|
+
name = kwargs.get("name", default_name or f"{agent_type.name.title()}")
|
|
477
|
+
instruction = kwargs.get("instruction", default_instruction or "")
|
|
478
|
+
servers = kwargs.get("servers", default_servers or [])
|
|
479
|
+
model = kwargs.get("model", None)
|
|
480
|
+
use_history = kwargs.get("use_history", default_use_history)
|
|
481
|
+
request_params = kwargs.get("request_params", None)
|
|
482
|
+
human_input = kwargs.get("human_input", False)
|
|
483
|
+
|
|
484
|
+
# Create base request params
|
|
485
|
+
def decorator(func: Callable) -> Callable:
|
|
486
|
+
# Create base request params
|
|
487
|
+
if (
|
|
488
|
+
request_params is not None
|
|
489
|
+
or model is not None
|
|
490
|
+
or use_history != default_use_history
|
|
491
|
+
):
|
|
492
|
+
max_tokens = 4096 if agent_type == AgentType.BASIC else None
|
|
493
|
+
params_dict = {"use_history": use_history, "model": model}
|
|
494
|
+
if max_tokens:
|
|
495
|
+
params_dict["maxTokens"] = max_tokens
|
|
496
|
+
if request_params:
|
|
497
|
+
params_dict.update(request_params)
|
|
498
|
+
base_params = RequestParams(**params_dict)
|
|
499
|
+
else:
|
|
500
|
+
base_params = RequestParams(use_history=use_history)
|
|
501
|
+
|
|
502
|
+
# Create agent configuration
|
|
503
|
+
config = AgentConfig(
|
|
504
|
+
name=name,
|
|
505
|
+
instruction=instruction,
|
|
506
|
+
servers=servers,
|
|
507
|
+
model=model,
|
|
508
|
+
use_history=use_history,
|
|
509
|
+
default_request_params=base_params,
|
|
510
|
+
human_input=human_input,
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
# Build agent/workflow specific data
|
|
514
|
+
agent_data = {
|
|
515
|
+
"config": config,
|
|
516
|
+
"type": agent_type.value,
|
|
517
|
+
"func": func,
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
# Add extra parameters specific to this agent type
|
|
521
|
+
for key, value in kwargs.items():
|
|
522
|
+
if key not in [
|
|
523
|
+
"name",
|
|
524
|
+
"instruction",
|
|
525
|
+
"servers",
|
|
526
|
+
"model",
|
|
527
|
+
"use_history",
|
|
528
|
+
"request_params",
|
|
529
|
+
"human_input",
|
|
530
|
+
]:
|
|
531
|
+
agent_data[key] = value
|
|
532
|
+
|
|
533
|
+
# Store the configuration under the agent name
|
|
534
|
+
self.agents[name] = agent_data
|
|
535
|
+
|
|
536
|
+
# Either wrap or return the original function
|
|
537
|
+
if wrapper_needed:
|
|
538
|
+
|
|
539
|
+
async def wrapper(*args, **kwargs):
|
|
540
|
+
return await func(*args, **kwargs)
|
|
541
|
+
|
|
542
|
+
return wrapper
|
|
543
|
+
return func
|
|
544
|
+
|
|
545
|
+
return decorator
|
|
546
|
+
|
|
547
|
+
return decorator_wrapper
|
|
548
|
+
|
|
429
549
|
def agent(
|
|
430
550
|
self,
|
|
431
551
|
name: str = "Agent",
|
|
432
552
|
*,
|
|
433
553
|
instruction: str = "You are a helpful agent.",
|
|
434
554
|
servers: List[str] = [],
|
|
435
|
-
model:
|
|
555
|
+
model: str | None = None,
|
|
436
556
|
use_history: bool = True,
|
|
437
557
|
request_params: Optional[Dict] = None,
|
|
438
558
|
human_input: bool = False,
|
|
@@ -447,46 +567,32 @@ class FastAgent(ContextDependent):
|
|
|
447
567
|
model: Model specification string (highest precedence)
|
|
448
568
|
use_history: Whether to maintain conversation history
|
|
449
569
|
request_params: Additional request parameters for the LLM
|
|
570
|
+
human_input: Whether to enable human input capabilities
|
|
450
571
|
"""
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
servers=servers,
|
|
466
|
-
model=model, # Highest precedence
|
|
467
|
-
use_history=use_history,
|
|
468
|
-
default_request_params=base_params,
|
|
469
|
-
human_input=human_input,
|
|
470
|
-
)
|
|
471
|
-
|
|
472
|
-
# Store the agent configuration
|
|
473
|
-
self.agents[name] = {
|
|
474
|
-
"config": config,
|
|
475
|
-
"type": AgentType.BASIC.value,
|
|
476
|
-
"func": func,
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
return func # Don't wrap the function, just return it
|
|
480
|
-
|
|
572
|
+
decorator = self._create_decorator(
|
|
573
|
+
AgentType.BASIC,
|
|
574
|
+
default_name="Agent",
|
|
575
|
+
default_instruction="You are a helpful agent.",
|
|
576
|
+
default_use_history=True,
|
|
577
|
+
)(
|
|
578
|
+
name=name,
|
|
579
|
+
instruction=instruction,
|
|
580
|
+
servers=servers,
|
|
581
|
+
model=model,
|
|
582
|
+
use_history=use_history,
|
|
583
|
+
request_params=request_params,
|
|
584
|
+
human_input=human_input,
|
|
585
|
+
)
|
|
481
586
|
return decorator
|
|
482
587
|
|
|
483
588
|
def orchestrator(
|
|
484
589
|
self,
|
|
485
|
-
name: str,
|
|
486
|
-
|
|
590
|
+
name: str = "Orchestrator",
|
|
591
|
+
*,
|
|
592
|
+
instruction: str | None = None,
|
|
487
593
|
agents: List[str],
|
|
488
594
|
model: str | None = None,
|
|
489
|
-
use_history: bool =
|
|
595
|
+
use_history: bool = False,
|
|
490
596
|
request_params: Optional[Dict] = None,
|
|
491
597
|
human_input: bool = False,
|
|
492
598
|
) -> Callable:
|
|
@@ -498,37 +604,31 @@ class FastAgent(ContextDependent):
|
|
|
498
604
|
instruction: Base instruction for the orchestrator
|
|
499
605
|
agents: List of agent names this orchestrator can use
|
|
500
606
|
model: Model specification string (highest precedence)
|
|
501
|
-
use_history: Whether to maintain conversation history
|
|
607
|
+
use_history: Whether to maintain conversation history (forced false)
|
|
502
608
|
request_params: Additional request parameters for the LLM
|
|
609
|
+
human_input: Whether to enable human input capabilities
|
|
503
610
|
"""
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
"child_agents": agents,
|
|
526
|
-
"type": AgentType.ORCHESTRATOR.value,
|
|
527
|
-
"func": func,
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
return func
|
|
531
|
-
|
|
611
|
+
default_instruction = """
|
|
612
|
+
You are an expert planner. Given an objective task and a list of MCP servers (which are collections of tools)
|
|
613
|
+
or Agents (which are collections of servers), your job is to break down the objective into a series of steps,
|
|
614
|
+
which can be performed by LLMs with access to the servers or agents.
|
|
615
|
+
"""
|
|
616
|
+
|
|
617
|
+
decorator = self._create_decorator(
|
|
618
|
+
AgentType.ORCHESTRATOR,
|
|
619
|
+
default_name="Orchestrator",
|
|
620
|
+
default_instruction=default_instruction,
|
|
621
|
+
default_servers=[],
|
|
622
|
+
default_use_history=False,
|
|
623
|
+
)(
|
|
624
|
+
name=name,
|
|
625
|
+
instruction=instruction,
|
|
626
|
+
child_agents=agents,
|
|
627
|
+
model=model,
|
|
628
|
+
use_history=use_history,
|
|
629
|
+
request_params=request_params,
|
|
630
|
+
human_input=human_input,
|
|
631
|
+
)
|
|
532
632
|
return decorator
|
|
533
633
|
|
|
534
634
|
def parallel(
|
|
@@ -553,33 +653,20 @@ class FastAgent(ContextDependent):
|
|
|
553
653
|
use_history: Whether to maintain conversation history
|
|
554
654
|
request_params: Additional request parameters for the LLM
|
|
555
655
|
"""
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
)
|
|
571
|
-
|
|
572
|
-
# Store the parallel configuration
|
|
573
|
-
self.agents[name] = {
|
|
574
|
-
"config": config,
|
|
575
|
-
"fan_out": fan_out,
|
|
576
|
-
"fan_in": fan_in,
|
|
577
|
-
"type": AgentType.PARALLEL.value,
|
|
578
|
-
"func": func,
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
return func
|
|
582
|
-
|
|
656
|
+
decorator = self._create_decorator(
|
|
657
|
+
AgentType.PARALLEL,
|
|
658
|
+
default_instruction="",
|
|
659
|
+
default_servers=[],
|
|
660
|
+
default_use_history=True,
|
|
661
|
+
)(
|
|
662
|
+
name=name,
|
|
663
|
+
fan_in=fan_in,
|
|
664
|
+
fan_out=fan_out,
|
|
665
|
+
instruction=instruction,
|
|
666
|
+
model=model,
|
|
667
|
+
use_history=use_history,
|
|
668
|
+
request_params=request_params,
|
|
669
|
+
)
|
|
583
670
|
return decorator
|
|
584
671
|
|
|
585
672
|
def evaluator_optimizer(
|
|
@@ -604,33 +691,21 @@ class FastAgent(ContextDependent):
|
|
|
604
691
|
use_history: Whether to maintain conversation history
|
|
605
692
|
request_params: Additional request parameters for the LLM
|
|
606
693
|
"""
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
"evaluator": evaluator,
|
|
623
|
-
"min_rating": min_rating,
|
|
624
|
-
"max_refinements": max_refinements,
|
|
625
|
-
"type": AgentType.EVALUATOR_OPTIMIZER.value,
|
|
626
|
-
"func": func,
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
async def wrapper(*args, **kwargs):
|
|
630
|
-
return await func(*args, **kwargs)
|
|
631
|
-
|
|
632
|
-
return wrapper
|
|
633
|
-
|
|
694
|
+
decorator = self._create_decorator(
|
|
695
|
+
AgentType.EVALUATOR_OPTIMIZER,
|
|
696
|
+
default_instruction="",
|
|
697
|
+
default_servers=[],
|
|
698
|
+
default_use_history=True,
|
|
699
|
+
wrapper_needed=True,
|
|
700
|
+
)(
|
|
701
|
+
name=name,
|
|
702
|
+
optimizer=optimizer,
|
|
703
|
+
evaluator=evaluator,
|
|
704
|
+
min_rating=min_rating,
|
|
705
|
+
max_refinements=max_refinements,
|
|
706
|
+
use_history=use_history,
|
|
707
|
+
request_params=request_params,
|
|
708
|
+
)
|
|
634
709
|
return decorator
|
|
635
710
|
|
|
636
711
|
def router(
|
|
@@ -649,79 +724,231 @@ class FastAgent(ContextDependent):
|
|
|
649
724
|
Args:
|
|
650
725
|
name: Name of the router
|
|
651
726
|
agents: List of agent names this router can delegate to
|
|
652
|
-
servers: List of server names the router can use directly
|
|
727
|
+
servers: List of server names the router can use directly (currently not supported)
|
|
653
728
|
model: Model specification string
|
|
654
729
|
use_history: Whether to maintain conversation history
|
|
655
730
|
request_params: Additional request parameters for the LLM
|
|
731
|
+
human_input: Whether to enable human input capabilities
|
|
656
732
|
"""
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
default_request_params=base_params,
|
|
672
|
-
human_input=human_input,
|
|
673
|
-
)
|
|
674
|
-
|
|
675
|
-
# Store the router configuration
|
|
676
|
-
self.agents[name] = {
|
|
677
|
-
"config": config,
|
|
678
|
-
"agents": agents,
|
|
679
|
-
"type": AgentType.ROUTER.value,
|
|
680
|
-
"func": func,
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
async def wrapper(*args, **kwargs):
|
|
684
|
-
return await func(*args, **kwargs)
|
|
685
|
-
|
|
686
|
-
return wrapper
|
|
687
|
-
|
|
733
|
+
decorator = self._create_decorator(
|
|
734
|
+
AgentType.ROUTER,
|
|
735
|
+
default_instruction="",
|
|
736
|
+
default_servers=[],
|
|
737
|
+
default_use_history=True,
|
|
738
|
+
wrapper_needed=True,
|
|
739
|
+
)(
|
|
740
|
+
name=name,
|
|
741
|
+
agents=agents,
|
|
742
|
+
model=model,
|
|
743
|
+
use_history=use_history,
|
|
744
|
+
request_params=request_params,
|
|
745
|
+
human_input=human_input,
|
|
746
|
+
)
|
|
688
747
|
return decorator
|
|
689
748
|
|
|
690
|
-
async def
|
|
749
|
+
async def _create_agents_by_type(
|
|
750
|
+
self,
|
|
751
|
+
agent_app: MCPApp,
|
|
752
|
+
agent_type: AgentType,
|
|
753
|
+
active_agents: ProxyDict = None,
|
|
754
|
+
**kwargs,
|
|
755
|
+
) -> ProxyDict:
|
|
691
756
|
"""
|
|
692
|
-
|
|
757
|
+
Generic method to create agents of a specific type.
|
|
693
758
|
|
|
694
759
|
Args:
|
|
695
760
|
agent_app: The main application instance
|
|
761
|
+
agent_type: Type of agents to create
|
|
762
|
+
active_agents: Dictionary of already created agents/proxies (for dependencies)
|
|
763
|
+
**kwargs: Additional type-specific parameters
|
|
696
764
|
|
|
697
765
|
Returns:
|
|
698
|
-
Dictionary of initialized
|
|
766
|
+
Dictionary of initialized agents wrapped in appropriate proxies
|
|
699
767
|
"""
|
|
700
|
-
active_agents
|
|
768
|
+
if active_agents is None:
|
|
769
|
+
active_agents = {}
|
|
770
|
+
|
|
771
|
+
# Create a dictionary to store the initialized agents
|
|
772
|
+
result_agents = {}
|
|
701
773
|
|
|
774
|
+
# Get all agents of the specified type
|
|
702
775
|
for name, agent_data in self.agents.items():
|
|
703
|
-
if agent_data["type"] ==
|
|
776
|
+
if agent_data["type"] == agent_type.value:
|
|
777
|
+
# Get common configuration
|
|
704
778
|
config = agent_data["config"]
|
|
705
779
|
|
|
706
|
-
#
|
|
707
|
-
|
|
780
|
+
# Type-specific initialization
|
|
781
|
+
if agent_type == AgentType.BASIC:
|
|
782
|
+
# Create basic agent with configuration
|
|
783
|
+
agent = Agent(config=config, context=agent_app.context)
|
|
784
|
+
|
|
785
|
+
# Set up LLM with proper configuration
|
|
786
|
+
async with agent:
|
|
787
|
+
llm_factory = self._get_model_factory(
|
|
788
|
+
model=config.model,
|
|
789
|
+
request_params=config.default_request_params,
|
|
790
|
+
)
|
|
791
|
+
agent._llm = await agent.attach_llm(llm_factory)
|
|
708
792
|
|
|
709
|
-
|
|
710
|
-
|
|
793
|
+
# Store the agent
|
|
794
|
+
instance = agent
|
|
795
|
+
|
|
796
|
+
elif agent_type == AgentType.ORCHESTRATOR:
|
|
797
|
+
# Get base params configured with model settings
|
|
798
|
+
base_params = (
|
|
799
|
+
config.default_request_params.model_copy()
|
|
800
|
+
if config.default_request_params
|
|
801
|
+
else RequestParams()
|
|
802
|
+
)
|
|
803
|
+
base_params.use_history = False # Force no history for orchestrator
|
|
804
|
+
|
|
805
|
+
# Get the child agents - need to unwrap proxies and validate LLM config
|
|
806
|
+
child_agents = []
|
|
807
|
+
for agent_name in agent_data["child_agents"]:
|
|
808
|
+
proxy = active_agents[agent_name]
|
|
809
|
+
instance = self._unwrap_proxy(proxy)
|
|
810
|
+
# Validate basic agents have LLM
|
|
811
|
+
if isinstance(instance, Agent):
|
|
812
|
+
if not hasattr(instance, "_llm") or not instance._llm:
|
|
813
|
+
raise AgentConfigError(
|
|
814
|
+
f"Agent '{agent_name}' used by orchestrator '{name}' missing LLM configuration",
|
|
815
|
+
"All agents must be fully configured with LLMs before being used in an orchestrator",
|
|
816
|
+
)
|
|
817
|
+
child_agents.append(instance)
|
|
818
|
+
|
|
819
|
+
# Create a properly configured planner agent
|
|
820
|
+
planner_config = AgentConfig(
|
|
821
|
+
name=f"{name}", # Use orchestrator name as prefix
|
|
822
|
+
instruction=config.instruction
|
|
823
|
+
or """
|
|
824
|
+
You are an expert planner. Given an objective task and a list of MCP servers (which are collections of tools)
|
|
825
|
+
or Agents (which are collections of servers), your job is to break down the objective into a series of steps,
|
|
826
|
+
which can be performed by LLMs with access to the servers or agents.
|
|
827
|
+
""",
|
|
828
|
+
servers=[], # Planner doesn't need server access
|
|
829
|
+
model=config.model, # Use same model as orchestrator
|
|
830
|
+
default_request_params=base_params,
|
|
831
|
+
)
|
|
832
|
+
planner_agent = Agent(
|
|
833
|
+
config=planner_config, context=agent_app.context
|
|
834
|
+
)
|
|
835
|
+
planner_factory = self._get_model_factory(
|
|
836
|
+
model=config.model,
|
|
837
|
+
request_params=config.default_request_params,
|
|
838
|
+
)
|
|
839
|
+
|
|
840
|
+
async with planner_agent:
|
|
841
|
+
planner = await planner_agent.attach_llm(planner_factory)
|
|
842
|
+
|
|
843
|
+
# Create the orchestrator with pre-configured planner
|
|
844
|
+
instance = Orchestrator(
|
|
845
|
+
name=config.name,
|
|
846
|
+
planner=planner, # Pass pre-configured planner
|
|
847
|
+
available_agents=child_agents,
|
|
848
|
+
context=agent_app.context,
|
|
849
|
+
request_params=planner.default_request_params, # Base params already include model settings
|
|
850
|
+
plan_type="full", # TODO -- support iterative plan type properly
|
|
851
|
+
verb=ProgressAction.PLANNING, # Using PLANNING instead of ORCHESTRATING
|
|
852
|
+
)
|
|
853
|
+
|
|
854
|
+
elif agent_type == AgentType.EVALUATOR_OPTIMIZER:
|
|
855
|
+
# Get the referenced agents - unwrap from proxies
|
|
856
|
+
optimizer = self._unwrap_proxy(
|
|
857
|
+
active_agents[agent_data["optimizer"]]
|
|
858
|
+
)
|
|
859
|
+
evaluator = self._unwrap_proxy(
|
|
860
|
+
active_agents[agent_data["evaluator"]]
|
|
861
|
+
)
|
|
862
|
+
|
|
863
|
+
if not optimizer or not evaluator:
|
|
864
|
+
raise ValueError(
|
|
865
|
+
f"Missing agents for workflow {name}: "
|
|
866
|
+
f"optimizer={agent_data['optimizer']}, "
|
|
867
|
+
f"evaluator={agent_data['evaluator']}"
|
|
868
|
+
)
|
|
869
|
+
|
|
870
|
+
# TODO: Remove legacy - factory usage is only needed for str evaluators
|
|
871
|
+
# Later this should only be passed when evaluator is a string
|
|
872
|
+
optimizer_model = (
|
|
873
|
+
optimizer.config.model if isinstance(optimizer, Agent) else None
|
|
874
|
+
)
|
|
875
|
+
instance = EvaluatorOptimizerLLM(
|
|
876
|
+
optimizer=optimizer,
|
|
877
|
+
evaluator=evaluator,
|
|
878
|
+
min_rating=QualityRating[agent_data["min_rating"]],
|
|
879
|
+
max_refinements=agent_data["max_refinements"],
|
|
880
|
+
llm_factory=self._get_model_factory(model=optimizer_model),
|
|
881
|
+
context=agent_app.context,
|
|
882
|
+
)
|
|
883
|
+
|
|
884
|
+
elif agent_type == AgentType.ROUTER:
|
|
885
|
+
# Get the router's agents - unwrap proxies
|
|
886
|
+
router_agents = self._get_agent_instances(
|
|
887
|
+
agent_data["agents"], active_agents
|
|
888
|
+
)
|
|
889
|
+
|
|
890
|
+
# Create the router with proper configuration
|
|
711
891
|
llm_factory = self._get_model_factory(
|
|
712
892
|
model=config.model,
|
|
713
893
|
request_params=config.default_request_params,
|
|
714
894
|
)
|
|
715
|
-
agent._llm = await agent.attach_llm(llm_factory)
|
|
716
895
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
896
|
+
instance = LLMRouter(
|
|
897
|
+
name=config.name,
|
|
898
|
+
llm_factory=llm_factory,
|
|
899
|
+
agents=router_agents,
|
|
900
|
+
server_names=config.servers,
|
|
901
|
+
context=agent_app.context,
|
|
902
|
+
default_request_params=config.default_request_params,
|
|
903
|
+
verb=ProgressAction.ROUTING, # Set verb for progress display
|
|
904
|
+
)
|
|
905
|
+
|
|
906
|
+
elif agent_type == AgentType.PARALLEL:
|
|
907
|
+
# Get fan-out agents (could be basic agents or other parallels)
|
|
908
|
+
fan_out_agents = self._get_agent_instances(
|
|
909
|
+
agent_data["fan_out"], active_agents
|
|
910
|
+
)
|
|
911
|
+
|
|
912
|
+
# Get fan-in agent - unwrap proxy
|
|
913
|
+
fan_in_agent = self._unwrap_proxy(
|
|
914
|
+
active_agents[agent_data["fan_in"]]
|
|
915
|
+
)
|
|
916
|
+
|
|
917
|
+
# Create the parallel workflow
|
|
918
|
+
llm_factory = self._get_model_factory(config.model)
|
|
919
|
+
instance = ParallelLLM(
|
|
920
|
+
name=config.name,
|
|
921
|
+
instruction=config.instruction,
|
|
922
|
+
fan_out_agents=fan_out_agents,
|
|
923
|
+
fan_in_agent=fan_in_agent,
|
|
924
|
+
context=agent_app.context,
|
|
925
|
+
llm_factory=llm_factory,
|
|
926
|
+
default_request_params=config.default_request_params,
|
|
927
|
+
)
|
|
928
|
+
|
|
929
|
+
else:
|
|
930
|
+
raise ValueError(f"Unsupported agent type: {agent_type}")
|
|
931
|
+
|
|
932
|
+
# Create the appropriate proxy and store in results
|
|
933
|
+
result_agents[name] = self._create_proxy(
|
|
934
|
+
name, instance, agent_type.value
|
|
720
935
|
)
|
|
721
936
|
|
|
722
|
-
return
|
|
937
|
+
return result_agents
|
|
723
938
|
|
|
724
|
-
def
|
|
939
|
+
async def _create_basic_agents(self, agent_app: MCPApp) -> ProxyDict:
|
|
940
|
+
"""
|
|
941
|
+
Create and initialize basic agents with their configurations.
|
|
942
|
+
|
|
943
|
+
Args:
|
|
944
|
+
agent_app: The main application instance
|
|
945
|
+
|
|
946
|
+
Returns:
|
|
947
|
+
Dictionary of initialized basic agents wrapped in appropriate proxies
|
|
948
|
+
"""
|
|
949
|
+
return await self._create_agents_by_type(agent_app, AgentType.BASIC)
|
|
950
|
+
|
|
951
|
+
async def _create_orchestrators(
|
|
725
952
|
self, agent_app: MCPApp, active_agents: ProxyDict
|
|
726
953
|
) -> ProxyDict:
|
|
727
954
|
"""
|
|
@@ -734,57 +961,9 @@ class FastAgent(ContextDependent):
|
|
|
734
961
|
Returns:
|
|
735
962
|
Dictionary of initialized orchestrator agents wrapped in appropriate proxies
|
|
736
963
|
"""
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
config = agent_data["config"]
|
|
741
|
-
|
|
742
|
-
# TODO: Remove legacy - This model/params setup should be in Agent class
|
|
743
|
-
# Resolve model alias if present
|
|
744
|
-
model_config = ModelFactory.parse_model_string(config.model)
|
|
745
|
-
resolved_model = model_config.model_name
|
|
746
|
-
|
|
747
|
-
# Start with existing params if available
|
|
748
|
-
if config.default_request_params:
|
|
749
|
-
base_params = config.default_request_params.model_copy()
|
|
750
|
-
# Update with orchestrator-specific settings
|
|
751
|
-
base_params.use_history = config.use_history
|
|
752
|
-
base_params.model = resolved_model
|
|
753
|
-
else:
|
|
754
|
-
base_params = RequestParams(
|
|
755
|
-
use_history=config.use_history, model=resolved_model
|
|
756
|
-
)
|
|
757
|
-
|
|
758
|
-
llm_factory = self._get_model_factory(
|
|
759
|
-
model=config.model, # Use original model string for factory creation
|
|
760
|
-
request_params=base_params,
|
|
761
|
-
)
|
|
762
|
-
|
|
763
|
-
# Get the child agents - need to unwrap proxies
|
|
764
|
-
child_agents = []
|
|
765
|
-
for agent_name in agent_data["child_agents"]:
|
|
766
|
-
proxy = active_agents[agent_name]
|
|
767
|
-
if isinstance(proxy, LLMAgentProxy):
|
|
768
|
-
child_agents.append(proxy._agent) # Get the actual Agent
|
|
769
|
-
else:
|
|
770
|
-
# Handle case where it might be another workflow
|
|
771
|
-
child_agents.append(proxy._workflow)
|
|
772
|
-
|
|
773
|
-
orchestrator = Orchestrator(
|
|
774
|
-
name=config.name,
|
|
775
|
-
instruction=config.instruction,
|
|
776
|
-
available_agents=child_agents,
|
|
777
|
-
context=agent_app.context,
|
|
778
|
-
llm_factory=llm_factory,
|
|
779
|
-
request_params=base_params, # Use our base params that include model
|
|
780
|
-
plan_type="full",
|
|
781
|
-
)
|
|
782
|
-
|
|
783
|
-
# Use factory to create appropriate proxy
|
|
784
|
-
orchestrators[name] = self._create_proxy(
|
|
785
|
-
name, orchestrator, AgentType.ORCHESTRATOR.value
|
|
786
|
-
)
|
|
787
|
-
return orchestrators
|
|
964
|
+
return await self._create_agents_by_type(
|
|
965
|
+
agent_app, AgentType.ORCHESTRATOR, active_agents
|
|
966
|
+
)
|
|
788
967
|
|
|
789
968
|
async def _create_evaluator_optimizers(
|
|
790
969
|
self, agent_app: MCPApp, active_agents: ProxyDict
|
|
@@ -799,39 +978,9 @@ class FastAgent(ContextDependent):
|
|
|
799
978
|
Returns:
|
|
800
979
|
Dictionary of initialized evaluator-optimizer workflows
|
|
801
980
|
"""
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
# Get the referenced agents - unwrap from proxies
|
|
806
|
-
optimizer = self._unwrap_proxy(active_agents[agent_data["optimizer"]])
|
|
807
|
-
evaluator = self._unwrap_proxy(active_agents[agent_data["evaluator"]])
|
|
808
|
-
|
|
809
|
-
if not optimizer or not evaluator:
|
|
810
|
-
raise ValueError(
|
|
811
|
-
f"Missing agents for workflow {name}: "
|
|
812
|
-
f"optimizer={agent_data['optimizer']}, "
|
|
813
|
-
f"evaluator={agent_data['evaluator']}"
|
|
814
|
-
)
|
|
815
|
-
|
|
816
|
-
# TODO: Remove legacy - factory usage is only needed for str evaluators
|
|
817
|
-
# Later this should only be passed when evaluator is a string
|
|
818
|
-
optimizer_model = (
|
|
819
|
-
optimizer.config.model if isinstance(optimizer, Agent) else None
|
|
820
|
-
)
|
|
821
|
-
workflow = EvaluatorOptimizerLLM(
|
|
822
|
-
optimizer=optimizer,
|
|
823
|
-
evaluator=evaluator,
|
|
824
|
-
min_rating=QualityRating[agent_data["min_rating"]],
|
|
825
|
-
max_refinements=agent_data["max_refinements"],
|
|
826
|
-
llm_factory=self._get_model_factory(model=optimizer_model),
|
|
827
|
-
context=agent_app.context,
|
|
828
|
-
)
|
|
829
|
-
|
|
830
|
-
workflows[name] = self._create_proxy(
|
|
831
|
-
name, workflow, AgentType.EVALUATOR_OPTIMIZER.value
|
|
832
|
-
)
|
|
833
|
-
|
|
834
|
-
return workflows
|
|
981
|
+
return await self._create_agents_by_type(
|
|
982
|
+
agent_app, AgentType.EVALUATOR_OPTIMIZER, active_agents
|
|
983
|
+
)
|
|
835
984
|
|
|
836
985
|
def _get_parallel_dependencies(
|
|
837
986
|
self, name: str, visited: set, path: set
|
|
@@ -878,7 +1027,7 @@ class FastAgent(ContextDependent):
|
|
|
878
1027
|
|
|
879
1028
|
return deps
|
|
880
1029
|
|
|
881
|
-
def _create_parallel_agents(
|
|
1030
|
+
async def _create_parallel_agents(
|
|
882
1031
|
self, agent_app: MCPApp, active_agents: ProxyDict
|
|
883
1032
|
) -> ProxyDict:
|
|
884
1033
|
"""
|
|
@@ -915,38 +1064,21 @@ class FastAgent(ContextDependent):
|
|
|
915
1064
|
# Create each agent in order
|
|
916
1065
|
for agent_name in ordered_agents:
|
|
917
1066
|
if agent_name not in parallel_agents:
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
)
|
|
925
|
-
|
|
926
|
-
# Get fan-in agent - unwrap proxy
|
|
927
|
-
fan_in_agent = self._unwrap_proxy(
|
|
928
|
-
active_agents[agent_data["fan_in"]]
|
|
929
|
-
)
|
|
930
|
-
|
|
931
|
-
# Create the parallel workflow
|
|
932
|
-
llm_factory = self._get_model_factory(config.model)
|
|
933
|
-
parallel = ParallelLLM(
|
|
934
|
-
name=config.name,
|
|
935
|
-
instruction=config.instruction,
|
|
936
|
-
fan_out_agents=fan_out_agents,
|
|
937
|
-
fan_in_agent=fan_in_agent,
|
|
938
|
-
context=agent_app.context,
|
|
939
|
-
llm_factory=llm_factory,
|
|
940
|
-
default_request_params=config.default_request_params,
|
|
941
|
-
)
|
|
942
|
-
|
|
943
|
-
parallel_agents[agent_name] = self._create_proxy(
|
|
944
|
-
name, parallel, AgentType.PARALLEL.value
|
|
1067
|
+
# Create one parallel agent at a time using the generic method
|
|
1068
|
+
agent_result = await self._create_agents_by_type(
|
|
1069
|
+
agent_app,
|
|
1070
|
+
AgentType.PARALLEL,
|
|
1071
|
+
active_agents,
|
|
1072
|
+
agent_name=agent_name,
|
|
945
1073
|
)
|
|
1074
|
+
if agent_name in agent_result:
|
|
1075
|
+
parallel_agents[agent_name] = agent_result[agent_name]
|
|
946
1076
|
|
|
947
1077
|
return parallel_agents
|
|
948
1078
|
|
|
949
|
-
def _create_routers(
|
|
1079
|
+
async def _create_routers(
|
|
1080
|
+
self, agent_app: MCPApp, active_agents: ProxyDict
|
|
1081
|
+
) -> ProxyDict:
|
|
950
1082
|
"""
|
|
951
1083
|
Create router agents.
|
|
952
1084
|
|
|
@@ -957,34 +1089,9 @@ class FastAgent(ContextDependent):
|
|
|
957
1089
|
Returns:
|
|
958
1090
|
Dictionary of initialized router agents
|
|
959
1091
|
"""
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
config = agent_data["config"]
|
|
964
|
-
|
|
965
|
-
# Get the router's agents - unwrap proxies
|
|
966
|
-
router_agents = self._get_agent_instances(
|
|
967
|
-
agent_data["agents"], active_agents
|
|
968
|
-
)
|
|
969
|
-
|
|
970
|
-
# Create the router with proper configuration
|
|
971
|
-
llm_factory = self._get_model_factory(
|
|
972
|
-
model=config.model,
|
|
973
|
-
request_params=config.default_request_params,
|
|
974
|
-
)
|
|
975
|
-
|
|
976
|
-
router = LLMRouter(
|
|
977
|
-
name=config.name, # Add the name parameter
|
|
978
|
-
llm_factory=llm_factory,
|
|
979
|
-
agents=router_agents,
|
|
980
|
-
server_names=config.servers,
|
|
981
|
-
context=agent_app.context,
|
|
982
|
-
default_request_params=config.default_request_params,
|
|
983
|
-
)
|
|
984
|
-
|
|
985
|
-
routers[name] = self._create_proxy(name, router, AgentType.ROUTER.value)
|
|
986
|
-
|
|
987
|
-
return routers
|
|
1092
|
+
return await self._create_agents_by_type(
|
|
1093
|
+
agent_app, AgentType.ROUTER, active_agents
|
|
1094
|
+
)
|
|
988
1095
|
|
|
989
1096
|
def _unwrap_proxy(self, proxy: BaseAgentProxy) -> AgentOrWorkflow:
|
|
990
1097
|
"""
|
|
@@ -1029,14 +1136,18 @@ class FastAgent(ContextDependent):
|
|
|
1029
1136
|
self._validate_server_references()
|
|
1030
1137
|
self._validate_workflow_references()
|
|
1031
1138
|
|
|
1032
|
-
# Create all types of agents
|
|
1139
|
+
# Create all types of agents in dependency order
|
|
1033
1140
|
active_agents = await self._create_basic_agents(agent_app)
|
|
1034
|
-
orchestrators = self._create_orchestrators(
|
|
1035
|
-
|
|
1141
|
+
orchestrators = await self._create_orchestrators(
|
|
1142
|
+
agent_app, active_agents
|
|
1143
|
+
)
|
|
1144
|
+
parallel_agents = await self._create_parallel_agents(
|
|
1145
|
+
agent_app, active_agents
|
|
1146
|
+
)
|
|
1036
1147
|
evaluator_optimizers = await self._create_evaluator_optimizers(
|
|
1037
1148
|
agent_app, active_agents
|
|
1038
1149
|
)
|
|
1039
|
-
routers = self._create_routers(agent_app, active_agents)
|
|
1150
|
+
routers = await self._create_routers(agent_app, active_agents)
|
|
1040
1151
|
|
|
1041
1152
|
# Merge all agents into active_agents
|
|
1042
1153
|
active_agents.update(orchestrators)
|
|
@@ -1050,48 +1161,38 @@ class FastAgent(ContextDependent):
|
|
|
1050
1161
|
|
|
1051
1162
|
except ServerConfigError as e:
|
|
1052
1163
|
had_error = True
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
print(e.details)
|
|
1058
|
-
print(
|
|
1059
|
-
"\nPlease check your 'fastagent.config.yaml' configuration file and add the missing server definitions."
|
|
1164
|
+
self._handle_error(
|
|
1165
|
+
e,
|
|
1166
|
+
"Server Configuration Error",
|
|
1167
|
+
"Please check your 'fastagent.config.yaml' configuration file and add the missing server definitions.",
|
|
1060
1168
|
)
|
|
1061
1169
|
raise SystemExit(1)
|
|
1062
1170
|
|
|
1063
1171
|
except ProviderKeyError as e:
|
|
1064
1172
|
had_error = True
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
print(e.details)
|
|
1070
|
-
print(
|
|
1071
|
-
"\nPlease check your 'fastagent.secrets.yaml' configuration file and ensure all required API keys are set."
|
|
1173
|
+
self._handle_error(
|
|
1174
|
+
e,
|
|
1175
|
+
"Provider Configuration Error",
|
|
1176
|
+
"Please check your 'fastagent.secrets.yaml' configuration file and ensure all required API keys are set.",
|
|
1072
1177
|
)
|
|
1073
1178
|
raise SystemExit(1)
|
|
1074
1179
|
|
|
1075
1180
|
except AgentConfigError as e:
|
|
1076
1181
|
had_error = True
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
print(e.details)
|
|
1082
|
-
print(
|
|
1083
|
-
"\nPlease check your agent definition and ensure names and references are correct."
|
|
1182
|
+
self._handle_error(
|
|
1183
|
+
e,
|
|
1184
|
+
"Workflow or Agent Configuration Error",
|
|
1185
|
+
"Please check your agent definition and ensure names and references are correct.",
|
|
1084
1186
|
)
|
|
1085
1187
|
raise SystemExit(1)
|
|
1086
1188
|
|
|
1087
1189
|
except ServerInitializationError as e:
|
|
1088
1190
|
had_error = True
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
print("\nThere was an error starting up the MCP Server.")
|
|
1191
|
+
self._handle_error(
|
|
1192
|
+
e,
|
|
1193
|
+
"Server Startup Error",
|
|
1194
|
+
"There was an error starting up the MCP Server.",
|
|
1195
|
+
)
|
|
1095
1196
|
raise SystemExit(1)
|
|
1096
1197
|
finally:
|
|
1097
1198
|
# Clean up any active agents without re-raising errors
|
|
@@ -1102,3 +1203,31 @@ class FastAgent(ContextDependent):
|
|
|
1102
1203
|
await proxy._agent.__aexit__(None, None, None)
|
|
1103
1204
|
except Exception:
|
|
1104
1205
|
pass # Ignore cleanup errors
|
|
1206
|
+
|
|
1207
|
+
def _handle_error(
|
|
1208
|
+
self, e: Exception, error_type: str, suggestion: str = None
|
|
1209
|
+
) -> None:
|
|
1210
|
+
"""
|
|
1211
|
+
Handle errors with consistent formatting and messaging.
|
|
1212
|
+
|
|
1213
|
+
Args:
|
|
1214
|
+
e: The exception that was raised
|
|
1215
|
+
error_type: Type of error to display
|
|
1216
|
+
suggestion: Optional suggestion message to display
|
|
1217
|
+
"""
|
|
1218
|
+
print(f"\n[bold red]{error_type}:")
|
|
1219
|
+
print(getattr(e, "message", str(e)))
|
|
1220
|
+
if hasattr(e, "details") and e.details:
|
|
1221
|
+
print("\nDetails:")
|
|
1222
|
+
print(e.details)
|
|
1223
|
+
if suggestion:
|
|
1224
|
+
print(f"\n{suggestion}")
|
|
1225
|
+
|
|
1226
|
+
def _log_agent_load(self, agent_name: str) -> None:
|
|
1227
|
+
self.app._logger.info(
|
|
1228
|
+
f"Loaded {agent_name}",
|
|
1229
|
+
data={
|
|
1230
|
+
"progress_action": ProgressAction.LOADED,
|
|
1231
|
+
"agent_name": agent_name,
|
|
1232
|
+
},
|
|
1233
|
+
)
|