fast-agent-mcp 0.0.9__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.9.dist-info → fast_agent_mcp-0.0.11.dist-info}/METADATA +15 -11
- {fast_agent_mcp-0.0.9.dist-info → fast_agent_mcp-0.0.11.dist-info}/RECORD +25 -24
- mcp_agent/app.py +4 -4
- mcp_agent/cli/commands/bootstrap.py +1 -0
- mcp_agent/cli/commands/setup.py +1 -1
- mcp_agent/core/fastagent.py +461 -367
- 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/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/orchestrator.py +1 -1
- 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 +5 -4
- mcp_agent/workflows/llm/model_factory.py +25 -11
- mcp_agent/workflows/orchestrator/orchestrator.py +68 -7
- mcp_agent/workflows/orchestrator/orchestrator_prompts.py +11 -6
- mcp_agent/workflows/router/router_llm.py +13 -2
- mcp_agent/resources/examples/workflows/fastagent.config.yaml +0 -9
- {fast_agent_mcp-0.0.9.dist-info → fast_agent_mcp-0.0.11.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.0.9.dist-info → fast_agent_mcp-0.0.11.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.0.9.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 (
|
|
@@ -128,7 +129,9 @@ class RouterProxy(BaseAgentProxy):
|
|
|
128
129
|
top_result = results[0]
|
|
129
130
|
if isinstance(top_result.result, Agent):
|
|
130
131
|
# Agent route - delegate to the agent
|
|
131
|
-
|
|
132
|
+
agent = top_result.result
|
|
133
|
+
|
|
134
|
+
return await agent._llm.generate_str(message)
|
|
132
135
|
elif isinstance(top_result.result, str):
|
|
133
136
|
# Server route - use the router directly
|
|
134
137
|
return "Tool call requested by router - not yet supported"
|
|
@@ -223,6 +226,34 @@ class FastAgent(ContextDependent):
|
|
|
223
226
|
Provides a simplified way to create and manage agents using decorators.
|
|
224
227
|
"""
|
|
225
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
|
+
|
|
226
257
|
def _create_proxy(
|
|
227
258
|
self, name: str, instance: AgentOrWorkflow, agent_type: str
|
|
228
259
|
) -> BaseAgentProxy:
|
|
@@ -239,6 +270,11 @@ class FastAgent(ContextDependent):
|
|
|
239
270
|
Raises:
|
|
240
271
|
TypeError: If instance type doesn't match expected type for agent_type
|
|
241
272
|
"""
|
|
273
|
+
if agent_type not in [
|
|
274
|
+
AgentType.PARALLEL.value,
|
|
275
|
+
AgentType.EVALUATOR_OPTIMIZER.value,
|
|
276
|
+
]:
|
|
277
|
+
self._log_agent_load(name)
|
|
242
278
|
if agent_type == AgentType.BASIC.value:
|
|
243
279
|
if not isinstance(instance, Agent):
|
|
244
280
|
raise TypeError(
|
|
@@ -272,34 +308,6 @@ class FastAgent(ContextDependent):
|
|
|
272
308
|
else:
|
|
273
309
|
raise ValueError(f"Unknown agent type: {agent_type}")
|
|
274
310
|
|
|
275
|
-
def __init__(self, name: str, config_path: Optional[str] = None):
|
|
276
|
-
"""
|
|
277
|
-
Initialize the decorator interface.
|
|
278
|
-
|
|
279
|
-
Args:
|
|
280
|
-
name: Name of the application
|
|
281
|
-
config_path: Optional path to config file
|
|
282
|
-
"""
|
|
283
|
-
# Initialize ContextDependent
|
|
284
|
-
super().__init__()
|
|
285
|
-
|
|
286
|
-
# Setup command line argument parsing
|
|
287
|
-
parser = argparse.ArgumentParser(description="MCP Agent Application")
|
|
288
|
-
parser.add_argument(
|
|
289
|
-
"--model",
|
|
290
|
-
help="Override the default model for all agents. Precedence is default < config_file < command line < constructor",
|
|
291
|
-
)
|
|
292
|
-
self.args = parser.parse_args()
|
|
293
|
-
|
|
294
|
-
self.name = name
|
|
295
|
-
self.config_path = config_path
|
|
296
|
-
self._load_config()
|
|
297
|
-
self.app = MCPApp(
|
|
298
|
-
name=name,
|
|
299
|
-
settings=Settings(**self.config) if hasattr(self, "config") else None,
|
|
300
|
-
)
|
|
301
|
-
self.agents: Dict[str, Dict[str, Any]] = {}
|
|
302
|
-
|
|
303
311
|
@property
|
|
304
312
|
def context(self):
|
|
305
313
|
"""Access the application context"""
|
|
@@ -440,6 +448,104 @@ class FastAgent(ContextDependent):
|
|
|
440
448
|
# Let model factory handle the model string parsing and setup
|
|
441
449
|
return ModelFactory.create_factory(model_spec, request_params=request_params)
|
|
442
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
|
+
|
|
443
549
|
def agent(
|
|
444
550
|
self,
|
|
445
551
|
name: str = "Agent",
|
|
@@ -461,37 +567,22 @@ class FastAgent(ContextDependent):
|
|
|
461
567
|
model: Model specification string (highest precedence)
|
|
462
568
|
use_history: Whether to maintain conversation history
|
|
463
569
|
request_params: Additional request parameters for the LLM
|
|
570
|
+
human_input: Whether to enable human input capabilities
|
|
464
571
|
"""
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
servers=servers,
|
|
480
|
-
model=model, # Highest precedence
|
|
481
|
-
use_history=use_history,
|
|
482
|
-
default_request_params=base_params,
|
|
483
|
-
human_input=human_input,
|
|
484
|
-
)
|
|
485
|
-
|
|
486
|
-
# Store the agent configuration
|
|
487
|
-
self.agents[name] = {
|
|
488
|
-
"config": config,
|
|
489
|
-
"type": AgentType.BASIC.value,
|
|
490
|
-
"func": func,
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
return func # Don't wrap the function, just return it
|
|
494
|
-
|
|
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
|
+
)
|
|
495
586
|
return decorator
|
|
496
587
|
|
|
497
588
|
def orchestrator(
|
|
@@ -515,35 +606,29 @@ class FastAgent(ContextDependent):
|
|
|
515
606
|
model: Model specification string (highest precedence)
|
|
516
607
|
use_history: Whether to maintain conversation history (forced false)
|
|
517
608
|
request_params: Additional request parameters for the LLM
|
|
609
|
+
human_input: Whether to enable human input capabilities
|
|
518
610
|
"""
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
"child_agents": agents,
|
|
541
|
-
"type": AgentType.ORCHESTRATOR.value,
|
|
542
|
-
"func": func,
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
return func
|
|
546
|
-
|
|
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
|
+
)
|
|
547
632
|
return decorator
|
|
548
633
|
|
|
549
634
|
def parallel(
|
|
@@ -568,33 +653,20 @@ class FastAgent(ContextDependent):
|
|
|
568
653
|
use_history: Whether to maintain conversation history
|
|
569
654
|
request_params: Additional request parameters for the LLM
|
|
570
655
|
"""
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
)
|
|
586
|
-
|
|
587
|
-
# Store the parallel configuration
|
|
588
|
-
self.agents[name] = {
|
|
589
|
-
"config": config,
|
|
590
|
-
"fan_out": fan_out,
|
|
591
|
-
"fan_in": fan_in,
|
|
592
|
-
"type": AgentType.PARALLEL.value,
|
|
593
|
-
"func": func,
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
return func
|
|
597
|
-
|
|
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
|
+
)
|
|
598
670
|
return decorator
|
|
599
671
|
|
|
600
672
|
def evaluator_optimizer(
|
|
@@ -619,33 +691,21 @@ class FastAgent(ContextDependent):
|
|
|
619
691
|
use_history: Whether to maintain conversation history
|
|
620
692
|
request_params: Additional request parameters for the LLM
|
|
621
693
|
"""
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
"evaluator": evaluator,
|
|
638
|
-
"min_rating": min_rating,
|
|
639
|
-
"max_refinements": max_refinements,
|
|
640
|
-
"type": AgentType.EVALUATOR_OPTIMIZER.value,
|
|
641
|
-
"func": func,
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
async def wrapper(*args, **kwargs):
|
|
645
|
-
return await func(*args, **kwargs)
|
|
646
|
-
|
|
647
|
-
return wrapper
|
|
648
|
-
|
|
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
|
+
)
|
|
649
709
|
return decorator
|
|
650
710
|
|
|
651
711
|
def router(
|
|
@@ -664,77 +724,229 @@ class FastAgent(ContextDependent):
|
|
|
664
724
|
Args:
|
|
665
725
|
name: Name of the router
|
|
666
726
|
agents: List of agent names this router can delegate to
|
|
667
|
-
servers: List of server names the router can use directly
|
|
727
|
+
servers: List of server names the router can use directly (currently not supported)
|
|
668
728
|
model: Model specification string
|
|
669
729
|
use_history: Whether to maintain conversation history
|
|
670
730
|
request_params: Additional request parameters for the LLM
|
|
731
|
+
human_input: Whether to enable human input capabilities
|
|
671
732
|
"""
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
default_request_params=base_params,
|
|
687
|
-
human_input=human_input,
|
|
688
|
-
)
|
|
689
|
-
|
|
690
|
-
# Store the router configuration
|
|
691
|
-
self.agents[name] = {
|
|
692
|
-
"config": config,
|
|
693
|
-
"agents": agents,
|
|
694
|
-
"type": AgentType.ROUTER.value,
|
|
695
|
-
"func": func,
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
async def wrapper(*args, **kwargs):
|
|
699
|
-
return await func(*args, **kwargs)
|
|
700
|
-
|
|
701
|
-
return wrapper
|
|
702
|
-
|
|
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
|
+
)
|
|
703
747
|
return decorator
|
|
704
748
|
|
|
705
|
-
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:
|
|
706
756
|
"""
|
|
707
|
-
|
|
757
|
+
Generic method to create agents of a specific type.
|
|
708
758
|
|
|
709
759
|
Args:
|
|
710
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
|
|
711
764
|
|
|
712
765
|
Returns:
|
|
713
|
-
Dictionary of initialized
|
|
766
|
+
Dictionary of initialized agents wrapped in appropriate proxies
|
|
714
767
|
"""
|
|
715
|
-
active_agents
|
|
768
|
+
if active_agents is None:
|
|
769
|
+
active_agents = {}
|
|
770
|
+
|
|
771
|
+
# Create a dictionary to store the initialized agents
|
|
772
|
+
result_agents = {}
|
|
716
773
|
|
|
774
|
+
# Get all agents of the specified type
|
|
717
775
|
for name, agent_data in self.agents.items():
|
|
718
|
-
if agent_data["type"] ==
|
|
776
|
+
if agent_data["type"] == agent_type.value:
|
|
777
|
+
# Get common configuration
|
|
719
778
|
config = agent_data["config"]
|
|
720
779
|
|
|
721
|
-
#
|
|
722
|
-
|
|
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)
|
|
792
|
+
|
|
793
|
+
# Store the agent
|
|
794
|
+
instance = agent
|
|
723
795
|
|
|
724
|
-
|
|
725
|
-
|
|
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
|
|
726
891
|
llm_factory = self._get_model_factory(
|
|
727
892
|
model=config.model,
|
|
728
893
|
request_params=config.default_request_params,
|
|
729
894
|
)
|
|
730
|
-
agent._llm = await agent.attach_llm(llm_factory)
|
|
731
895
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
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
|
|
735
935
|
)
|
|
736
936
|
|
|
737
|
-
return
|
|
937
|
+
return result_agents
|
|
938
|
+
|
|
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)
|
|
738
950
|
|
|
739
951
|
async def _create_orchestrators(
|
|
740
952
|
self, agent_app: MCPApp, active_agents: ProxyDict
|
|
@@ -749,71 +961,9 @@ class FastAgent(ContextDependent):
|
|
|
749
961
|
Returns:
|
|
750
962
|
Dictionary of initialized orchestrator agents wrapped in appropriate proxies
|
|
751
963
|
"""
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
config = agent_data["config"]
|
|
756
|
-
|
|
757
|
-
# Get base params configured with model settings
|
|
758
|
-
base_params = (
|
|
759
|
-
config.default_request_params.model_copy()
|
|
760
|
-
if config.default_request_params
|
|
761
|
-
else RequestParams()
|
|
762
|
-
)
|
|
763
|
-
base_params.use_history = False # Force no history for orchestrator
|
|
764
|
-
|
|
765
|
-
# Get the child agents - need to unwrap proxies and validate LLM config
|
|
766
|
-
child_agents = []
|
|
767
|
-
for agent_name in agent_data["child_agents"]:
|
|
768
|
-
proxy = active_agents[agent_name]
|
|
769
|
-
instance = self._unwrap_proxy(proxy)
|
|
770
|
-
# Validate basic agents have LLM
|
|
771
|
-
if isinstance(instance, Agent):
|
|
772
|
-
if not hasattr(instance, "_llm") or not instance._llm:
|
|
773
|
-
raise AgentConfigError(
|
|
774
|
-
f"Agent '{agent_name}' used by orchestrator '{name}' missing LLM configuration",
|
|
775
|
-
"All agents must be fully configured with LLMs before being used in an orchestrator",
|
|
776
|
-
)
|
|
777
|
-
child_agents.append(instance)
|
|
778
|
-
|
|
779
|
-
# Create a properly configured planner agent
|
|
780
|
-
planner_config = AgentConfig(
|
|
781
|
-
name=f"{name}", # Use orchestrator name as prefix
|
|
782
|
-
instruction=config.instruction
|
|
783
|
-
or """
|
|
784
|
-
You are an expert planner. Given an objective task and a list of MCP servers (which are collections of tools)
|
|
785
|
-
or Agents (which are collections of servers), your job is to break down the objective into a series of steps,
|
|
786
|
-
which can be performed by LLMs with access to the servers or agents.
|
|
787
|
-
""",
|
|
788
|
-
servers=[], # Planner doesn't need server access
|
|
789
|
-
model=config.model, # Use same model as orchestrator
|
|
790
|
-
default_request_params=base_params,
|
|
791
|
-
)
|
|
792
|
-
planner_agent = Agent(config=planner_config, context=agent_app.context)
|
|
793
|
-
planner_factory = self._get_model_factory(
|
|
794
|
-
model=config.model,
|
|
795
|
-
request_params=config.default_request_params,
|
|
796
|
-
)
|
|
797
|
-
|
|
798
|
-
async with planner_agent:
|
|
799
|
-
planner = await planner_agent.attach_llm(planner_factory)
|
|
800
|
-
|
|
801
|
-
# Create the orchestrator with pre-configured planner
|
|
802
|
-
orchestrator = Orchestrator(
|
|
803
|
-
name=config.name,
|
|
804
|
-
planner=planner, # Pass pre-configured planner
|
|
805
|
-
available_agents=child_agents,
|
|
806
|
-
context=agent_app.context,
|
|
807
|
-
request_params=planner.default_request_params, # Base params already include model settings
|
|
808
|
-
plan_type="full",
|
|
809
|
-
)
|
|
810
|
-
|
|
811
|
-
# Use factory to create appropriate proxy
|
|
812
|
-
orchestrators[name] = self._create_proxy(
|
|
813
|
-
name, orchestrator, AgentType.ORCHESTRATOR.value
|
|
814
|
-
)
|
|
815
|
-
|
|
816
|
-
return orchestrators
|
|
964
|
+
return await self._create_agents_by_type(
|
|
965
|
+
agent_app, AgentType.ORCHESTRATOR, active_agents
|
|
966
|
+
)
|
|
817
967
|
|
|
818
968
|
async def _create_evaluator_optimizers(
|
|
819
969
|
self, agent_app: MCPApp, active_agents: ProxyDict
|
|
@@ -828,39 +978,9 @@ class FastAgent(ContextDependent):
|
|
|
828
978
|
Returns:
|
|
829
979
|
Dictionary of initialized evaluator-optimizer workflows
|
|
830
980
|
"""
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
# Get the referenced agents - unwrap from proxies
|
|
835
|
-
optimizer = self._unwrap_proxy(active_agents[agent_data["optimizer"]])
|
|
836
|
-
evaluator = self._unwrap_proxy(active_agents[agent_data["evaluator"]])
|
|
837
|
-
|
|
838
|
-
if not optimizer or not evaluator:
|
|
839
|
-
raise ValueError(
|
|
840
|
-
f"Missing agents for workflow {name}: "
|
|
841
|
-
f"optimizer={agent_data['optimizer']}, "
|
|
842
|
-
f"evaluator={agent_data['evaluator']}"
|
|
843
|
-
)
|
|
844
|
-
|
|
845
|
-
# TODO: Remove legacy - factory usage is only needed for str evaluators
|
|
846
|
-
# Later this should only be passed when evaluator is a string
|
|
847
|
-
optimizer_model = (
|
|
848
|
-
optimizer.config.model if isinstance(optimizer, Agent) else None
|
|
849
|
-
)
|
|
850
|
-
workflow = EvaluatorOptimizerLLM(
|
|
851
|
-
optimizer=optimizer,
|
|
852
|
-
evaluator=evaluator,
|
|
853
|
-
min_rating=QualityRating[agent_data["min_rating"]],
|
|
854
|
-
max_refinements=agent_data["max_refinements"],
|
|
855
|
-
llm_factory=self._get_model_factory(model=optimizer_model),
|
|
856
|
-
context=agent_app.context,
|
|
857
|
-
)
|
|
858
|
-
|
|
859
|
-
workflows[name] = self._create_proxy(
|
|
860
|
-
name, workflow, AgentType.EVALUATOR_OPTIMIZER.value
|
|
861
|
-
)
|
|
862
|
-
|
|
863
|
-
return workflows
|
|
981
|
+
return await self._create_agents_by_type(
|
|
982
|
+
agent_app, AgentType.EVALUATOR_OPTIMIZER, active_agents
|
|
983
|
+
)
|
|
864
984
|
|
|
865
985
|
def _get_parallel_dependencies(
|
|
866
986
|
self, name: str, visited: set, path: set
|
|
@@ -944,34 +1064,15 @@ class FastAgent(ContextDependent):
|
|
|
944
1064
|
# Create each agent in order
|
|
945
1065
|
for agent_name in ordered_agents:
|
|
946
1066
|
if agent_name not in parallel_agents:
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
)
|
|
954
|
-
|
|
955
|
-
# Get fan-in agent - unwrap proxy
|
|
956
|
-
fan_in_agent = self._unwrap_proxy(
|
|
957
|
-
active_agents[agent_data["fan_in"]]
|
|
958
|
-
)
|
|
959
|
-
|
|
960
|
-
# Create the parallel workflow
|
|
961
|
-
llm_factory = self._get_model_factory(config.model)
|
|
962
|
-
parallel = ParallelLLM(
|
|
963
|
-
name=config.name,
|
|
964
|
-
instruction=config.instruction,
|
|
965
|
-
fan_out_agents=fan_out_agents,
|
|
966
|
-
fan_in_agent=fan_in_agent,
|
|
967
|
-
context=agent_app.context,
|
|
968
|
-
llm_factory=llm_factory,
|
|
969
|
-
default_request_params=config.default_request_params,
|
|
970
|
-
)
|
|
971
|
-
|
|
972
|
-
parallel_agents[agent_name] = self._create_proxy(
|
|
973
|
-
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,
|
|
974
1073
|
)
|
|
1074
|
+
if agent_name in agent_result:
|
|
1075
|
+
parallel_agents[agent_name] = agent_result[agent_name]
|
|
975
1076
|
|
|
976
1077
|
return parallel_agents
|
|
977
1078
|
|
|
@@ -988,34 +1089,9 @@ class FastAgent(ContextDependent):
|
|
|
988
1089
|
Returns:
|
|
989
1090
|
Dictionary of initialized router agents
|
|
990
1091
|
"""
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
config = agent_data["config"]
|
|
995
|
-
|
|
996
|
-
# Get the router's agents - unwrap proxies
|
|
997
|
-
router_agents = self._get_agent_instances(
|
|
998
|
-
agent_data["agents"], active_agents
|
|
999
|
-
)
|
|
1000
|
-
|
|
1001
|
-
# Create the router with proper configuration
|
|
1002
|
-
llm_factory = self._get_model_factory(
|
|
1003
|
-
model=config.model,
|
|
1004
|
-
request_params=config.default_request_params,
|
|
1005
|
-
)
|
|
1006
|
-
|
|
1007
|
-
router = LLMRouter(
|
|
1008
|
-
name=config.name, # Add the name parameter
|
|
1009
|
-
llm_factory=llm_factory,
|
|
1010
|
-
agents=router_agents,
|
|
1011
|
-
server_names=config.servers,
|
|
1012
|
-
context=agent_app.context,
|
|
1013
|
-
default_request_params=config.default_request_params,
|
|
1014
|
-
)
|
|
1015
|
-
|
|
1016
|
-
routers[name] = self._create_proxy(name, router, AgentType.ROUTER.value)
|
|
1017
|
-
|
|
1018
|
-
return routers
|
|
1092
|
+
return await self._create_agents_by_type(
|
|
1093
|
+
agent_app, AgentType.ROUTER, active_agents
|
|
1094
|
+
)
|
|
1019
1095
|
|
|
1020
1096
|
def _unwrap_proxy(self, proxy: BaseAgentProxy) -> AgentOrWorkflow:
|
|
1021
1097
|
"""
|
|
@@ -1085,48 +1161,38 @@ class FastAgent(ContextDependent):
|
|
|
1085
1161
|
|
|
1086
1162
|
except ServerConfigError as e:
|
|
1087
1163
|
had_error = True
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
print(e.details)
|
|
1093
|
-
print(
|
|
1094
|
-
"\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.",
|
|
1095
1168
|
)
|
|
1096
1169
|
raise SystemExit(1)
|
|
1097
1170
|
|
|
1098
1171
|
except ProviderKeyError as e:
|
|
1099
1172
|
had_error = True
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
print(e.details)
|
|
1105
|
-
print(
|
|
1106
|
-
"\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.",
|
|
1107
1177
|
)
|
|
1108
1178
|
raise SystemExit(1)
|
|
1109
1179
|
|
|
1110
1180
|
except AgentConfigError as e:
|
|
1111
1181
|
had_error = True
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
print(e.details)
|
|
1117
|
-
print(
|
|
1118
|
-
"\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.",
|
|
1119
1186
|
)
|
|
1120
1187
|
raise SystemExit(1)
|
|
1121
1188
|
|
|
1122
1189
|
except ServerInitializationError as e:
|
|
1123
1190
|
had_error = True
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
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
|
+
)
|
|
1130
1196
|
raise SystemExit(1)
|
|
1131
1197
|
finally:
|
|
1132
1198
|
# Clean up any active agents without re-raising errors
|
|
@@ -1137,3 +1203,31 @@ class FastAgent(ContextDependent):
|
|
|
1137
1203
|
await proxy._agent.__aexit__(None, None, None)
|
|
1138
1204
|
except Exception:
|
|
1139
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
|
+
)
|