fast-agent-mcp 0.1.11__py3-none-any.whl → 0.1.13__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.11.dist-info → fast_agent_mcp-0.1.13.dist-info}/METADATA +1 -1
- fast_agent_mcp-0.1.13.dist-info/RECORD +164 -0
- mcp_agent/agents/agent.py +37 -102
- mcp_agent/app.py +16 -27
- mcp_agent/cli/commands/bootstrap.py +22 -52
- mcp_agent/cli/commands/config.py +4 -4
- mcp_agent/cli/commands/setup.py +11 -26
- mcp_agent/cli/main.py +6 -9
- mcp_agent/cli/terminal.py +2 -2
- mcp_agent/config.py +1 -5
- mcp_agent/context.py +13 -26
- mcp_agent/context_dependent.py +3 -7
- mcp_agent/core/agent_app.py +46 -122
- mcp_agent/core/agent_types.py +29 -2
- mcp_agent/core/agent_utils.py +3 -5
- mcp_agent/core/decorators.py +6 -14
- mcp_agent/core/enhanced_prompt.py +25 -52
- mcp_agent/core/error_handling.py +1 -1
- mcp_agent/core/exceptions.py +8 -8
- mcp_agent/core/factory.py +30 -72
- mcp_agent/core/fastagent.py +48 -88
- mcp_agent/core/mcp_content.py +10 -19
- mcp_agent/core/prompt.py +8 -15
- mcp_agent/core/proxies.py +34 -25
- mcp_agent/core/request_params.py +46 -0
- mcp_agent/core/types.py +6 -6
- mcp_agent/core/validation.py +16 -16
- mcp_agent/executor/decorator_registry.py +11 -23
- mcp_agent/executor/executor.py +8 -17
- mcp_agent/executor/task_registry.py +2 -4
- mcp_agent/executor/temporal.py +28 -74
- mcp_agent/executor/workflow.py +3 -5
- mcp_agent/executor/workflow_signal.py +17 -29
- mcp_agent/human_input/handler.py +4 -9
- mcp_agent/human_input/types.py +2 -3
- 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 +15 -17
- mcp_agent/logging/rich_progress.py +10 -8
- mcp_agent/logging/tracing.py +4 -6
- mcp_agent/logging/transport.py +24 -24
- mcp_agent/mcp/gen_client.py +4 -12
- mcp_agent/mcp/interfaces.py +107 -88
- mcp_agent/mcp/mcp_agent_client_session.py +11 -19
- mcp_agent/mcp/mcp_agent_server.py +8 -10
- mcp_agent/mcp/mcp_aggregator.py +49 -122
- mcp_agent/mcp/mcp_connection_manager.py +16 -37
- mcp_agent/mcp/prompt_message_multipart.py +12 -18
- mcp_agent/mcp/prompt_serialization.py +13 -38
- mcp_agent/mcp/prompts/prompt_load.py +99 -0
- mcp_agent/mcp/prompts/prompt_server.py +21 -128
- mcp_agent/mcp/prompts/prompt_template.py +20 -42
- mcp_agent/mcp/resource_utils.py +8 -17
- mcp_agent/mcp/sampling.py +62 -64
- mcp_agent/mcp/stdio.py +11 -8
- mcp_agent/mcp_server/__init__.py +1 -1
- mcp_agent/mcp_server/agent_server.py +10 -17
- mcp_agent/mcp_server_registry.py +13 -35
- mcp_agent/resources/examples/data-analysis/analysis-campaign.py +1 -1
- mcp_agent/resources/examples/data-analysis/analysis.py +1 -1
- mcp_agent/resources/examples/data-analysis/slides.py +110 -0
- mcp_agent/resources/examples/internal/agent.py +2 -1
- 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/mcp_researcher/researcher-eval.py +1 -1
- mcp_agent/resources/examples/prompting/__init__.py +1 -1
- mcp_agent/resources/examples/prompting/agent.py +2 -1
- mcp_agent/resources/examples/prompting/image_server.py +5 -11
- mcp_agent/resources/examples/researcher/researcher-eval.py +1 -1
- mcp_agent/resources/examples/researcher/researcher-imp.py +3 -4
- mcp_agent/resources/examples/researcher/researcher.py +2 -1
- mcp_agent/resources/examples/workflows/agent_build.py +2 -1
- mcp_agent/resources/examples/workflows/chaining.py +2 -1
- mcp_agent/resources/examples/workflows/evaluator.py +2 -1
- mcp_agent/resources/examples/workflows/human_input.py +2 -1
- mcp_agent/resources/examples/workflows/orchestrator.py +2 -1
- mcp_agent/resources/examples/workflows/parallel.py +2 -1
- mcp_agent/resources/examples/workflows/router.py +2 -1
- mcp_agent/resources/examples/workflows/sse.py +1 -1
- mcp_agent/telemetry/usage_tracking.py +2 -1
- mcp_agent/ui/console_display.py +17 -41
- mcp_agent/workflows/embedding/embedding_base.py +1 -4
- mcp_agent/workflows/embedding/embedding_cohere.py +2 -2
- mcp_agent/workflows/embedding/embedding_openai.py +4 -13
- mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +23 -57
- mcp_agent/workflows/intent_classifier/intent_classifier_base.py +5 -8
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding.py +7 -11
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding_cohere.py +4 -8
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding_openai.py +4 -8
- mcp_agent/workflows/intent_classifier/intent_classifier_llm.py +11 -22
- mcp_agent/workflows/intent_classifier/intent_classifier_llm_anthropic.py +3 -3
- mcp_agent/workflows/intent_classifier/intent_classifier_llm_openai.py +4 -6
- mcp_agent/workflows/llm/anthropic_utils.py +8 -29
- mcp_agent/workflows/llm/augmented_llm.py +94 -332
- mcp_agent/workflows/llm/augmented_llm_anthropic.py +43 -76
- mcp_agent/workflows/llm/augmented_llm_openai.py +46 -100
- mcp_agent/workflows/llm/augmented_llm_passthrough.py +42 -20
- mcp_agent/workflows/llm/augmented_llm_playback.py +8 -6
- mcp_agent/workflows/llm/memory.py +103 -0
- mcp_agent/workflows/llm/model_factory.py +9 -21
- mcp_agent/workflows/llm/openai_utils.py +1 -1
- mcp_agent/workflows/llm/prompt_utils.py +39 -27
- mcp_agent/workflows/llm/providers/multipart_converter_anthropic.py +246 -184
- mcp_agent/workflows/llm/providers/multipart_converter_openai.py +212 -202
- mcp_agent/workflows/llm/providers/openai_multipart.py +19 -61
- mcp_agent/workflows/llm/providers/sampling_converter_anthropic.py +11 -212
- mcp_agent/workflows/llm/providers/sampling_converter_openai.py +13 -215
- mcp_agent/workflows/llm/sampling_converter.py +117 -0
- mcp_agent/workflows/llm/sampling_format_converter.py +12 -29
- mcp_agent/workflows/orchestrator/orchestrator.py +24 -67
- mcp_agent/workflows/orchestrator/orchestrator_models.py +14 -40
- mcp_agent/workflows/parallel/fan_in.py +17 -47
- mcp_agent/workflows/parallel/fan_out.py +6 -12
- mcp_agent/workflows/parallel/parallel_llm.py +9 -26
- mcp_agent/workflows/router/router_base.py +29 -59
- mcp_agent/workflows/router/router_embedding.py +11 -25
- mcp_agent/workflows/router/router_embedding_cohere.py +2 -2
- mcp_agent/workflows/router/router_embedding_openai.py +2 -2
- mcp_agent/workflows/router/router_llm.py +12 -28
- mcp_agent/workflows/swarm/swarm.py +20 -48
- mcp_agent/workflows/swarm/swarm_anthropic.py +2 -2
- mcp_agent/workflows/swarm/swarm_openai.py +2 -2
- fast_agent_mcp-0.1.11.dist-info/RECORD +0 -160
- mcp_agent/workflows/llm/llm_selector.py +0 -345
- {fast_agent_mcp-0.1.11.dist-info → fast_agent_mcp-0.1.13.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.1.11.dist-info → fast_agent_mcp-0.1.13.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.1.11.dist-info → fast_agent_mcp-0.1.13.dist-info}/licenses/LICENSE +0 -0
@@ -2,15 +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
14
|
from prompt_toolkit.lexers import PygmentsLexer
|
13
|
-
from prompt_toolkit.filters import Condition
|
14
15
|
from prompt_toolkit.styles import Style
|
15
16
|
from pygments.lexers.python import PythonLexer
|
16
17
|
from rich import print as rich_print
|
@@ -45,7 +46,7 @@ class AgentCompleter(Completer):
|
|
45
46
|
commands: List[str] = None,
|
46
47
|
agent_types: dict = None,
|
47
48
|
is_human_input: bool = False,
|
48
|
-
):
|
49
|
+
) -> None:
|
49
50
|
self.agents = agents
|
50
51
|
# Map commands to their descriptions for better completion hints
|
51
52
|
self.commands = {
|
@@ -61,9 +62,7 @@ class AgentCompleter(Completer):
|
|
61
62
|
if is_human_input:
|
62
63
|
self.commands.pop("agents")
|
63
64
|
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
|
65
|
+
self.commands.pop("prompt", None) # Remove prompt command in human input mode
|
67
66
|
self.agent_types = agent_types or {}
|
68
67
|
|
69
68
|
def get_completions(self, document, complete_event):
|
@@ -103,23 +102,23 @@ def create_keybindings(on_toggle_multiline=None, app=None):
|
|
103
102
|
kb = KeyBindings()
|
104
103
|
|
105
104
|
@kb.add("c-m", filter=Condition(lambda: not in_multiline_mode))
|
106
|
-
def _(event):
|
105
|
+
def _(event) -> None:
|
107
106
|
"""Enter: accept input when not in multiline mode."""
|
108
107
|
event.current_buffer.validate_and_handle()
|
109
108
|
|
110
109
|
@kb.add("c-m", filter=Condition(lambda: in_multiline_mode))
|
111
|
-
def _(event):
|
110
|
+
def _(event) -> None:
|
112
111
|
"""Enter: insert newline when in multiline mode."""
|
113
112
|
event.current_buffer.insert_text("\n")
|
114
113
|
|
115
114
|
# Use c-j (Ctrl+J) as an alternative to represent Ctrl+Enter in multiline mode
|
116
115
|
@kb.add("c-j", filter=Condition(lambda: in_multiline_mode))
|
117
|
-
def _(event):
|
116
|
+
def _(event) -> None:
|
118
117
|
"""Ctrl+J (equivalent to Ctrl+Enter): Submit in multiline mode."""
|
119
118
|
event.current_buffer.validate_and_handle()
|
120
119
|
|
121
120
|
@kb.add("c-t")
|
122
|
-
def _(event):
|
121
|
+
def _(event) -> None:
|
123
122
|
"""Ctrl+T: Toggle multiline mode."""
|
124
123
|
global in_multiline_mode
|
125
124
|
in_multiline_mode = not in_multiline_mode
|
@@ -138,7 +137,7 @@ def create_keybindings(on_toggle_multiline=None, app=None):
|
|
138
137
|
# The toolbar will show the current mode
|
139
138
|
|
140
139
|
@kb.add("c-l")
|
141
|
-
def _(event):
|
140
|
+
def _(event) -> None:
|
142
141
|
"""Ctrl+L: Clear the input buffer."""
|
143
142
|
event.current_buffer.text = ""
|
144
143
|
|
@@ -187,7 +186,7 @@ async def get_enhanced_input(
|
|
187
186
|
agent_histories[agent_name] = InMemoryHistory()
|
188
187
|
|
189
188
|
# Define callback for multiline toggle
|
190
|
-
def on_multiline_toggle(enabled):
|
189
|
+
def on_multiline_toggle(enabled) -> None:
|
191
190
|
nonlocal session
|
192
191
|
if hasattr(session, "app") and session.app:
|
193
192
|
session.app.invalidate()
|
@@ -209,9 +208,7 @@ async def get_enhanced_input(
|
|
209
208
|
("↑/↓", "History"),
|
210
209
|
]
|
211
210
|
|
212
|
-
newline =
|
213
|
-
"Ctrl+<Enter>:Submit" if in_multiline_mode else "<Enter>:Submit"
|
214
|
-
)
|
211
|
+
newline = "Ctrl+<Enter>:Submit" if in_multiline_mode else "<Enter>:Submit"
|
215
212
|
|
216
213
|
# Only show relevant shortcuts based on mode
|
217
214
|
shortcuts = [(k, v) for k, v in shortcuts if v]
|
@@ -250,9 +247,7 @@ async def get_enhanced_input(
|
|
250
247
|
)
|
251
248
|
|
252
249
|
# Create key bindings with a reference to the app
|
253
|
-
bindings = create_keybindings(
|
254
|
-
on_toggle_multiline=on_multiline_toggle, app=session.app
|
255
|
-
)
|
250
|
+
bindings = create_keybindings(on_toggle_multiline=on_multiline_toggle, app=session.app)
|
256
251
|
session.app.key_bindings = bindings
|
257
252
|
|
258
253
|
# Create formatted prompt text
|
@@ -267,20 +262,14 @@ async def get_enhanced_input(
|
|
267
262
|
if default == "STOP":
|
268
263
|
rich_print("Enter a prompt, [red]STOP[/red] to finish")
|
269
264
|
if default:
|
270
|
-
rich_print(
|
271
|
-
f"Press <ENTER> to use the default prompt:\n[cyan]{default}[/cyan]"
|
272
|
-
)
|
265
|
+
rich_print(f"Press <ENTER> to use the default prompt:\n[cyan]{default}[/cyan]")
|
273
266
|
|
274
267
|
# Mention available features but only on first usage globally
|
275
268
|
if not help_message_shown:
|
276
269
|
if is_human_input:
|
277
|
-
rich_print(
|
278
|
-
"[dim]Type /help for commands. Ctrl+T toggles multiline mode.[/dim]"
|
279
|
-
)
|
270
|
+
rich_print("[dim]Type /help for commands. Ctrl+T toggles multiline mode.[/dim]")
|
280
271
|
else:
|
281
|
-
rich_print(
|
282
|
-
"[dim]Type /help for commands, @agent to switch agent. Ctrl+T toggles multiline mode.[/dim]"
|
283
|
-
)
|
272
|
+
rich_print("[dim]Type /help for commands, @agent to switch agent. Ctrl+T toggles multiline mode.[/dim]")
|
284
273
|
rich_print()
|
285
274
|
help_message_shown = True
|
286
275
|
|
@@ -366,9 +355,7 @@ async def get_selection_input(
|
|
366
355
|
|
367
356
|
try:
|
368
357
|
# Get user input
|
369
|
-
selection = await prompt_session.prompt_async(
|
370
|
-
prompt_text, default=default or ""
|
371
|
-
)
|
358
|
+
selection = await prompt_session.prompt_async(prompt_text, default=default or "")
|
372
359
|
|
373
360
|
# Handle cancellation
|
374
361
|
if allow_cancel and not selection.strip():
|
@@ -409,9 +396,7 @@ async def get_argument_input(
|
|
409
396
|
if description:
|
410
397
|
rich_print(f" [dim]{arg_name}: {description}[/dim]")
|
411
398
|
|
412
|
-
prompt_text = HTML(
|
413
|
-
f"Enter value for <ansibrightcyan>{arg_name}</ansibrightcyan> {required_text}: "
|
414
|
-
)
|
399
|
+
prompt_text = HTML(f"Enter value for <ansibrightcyan>{arg_name}</ansibrightcyan> {required_text}: ")
|
415
400
|
|
416
401
|
# Create prompt session
|
417
402
|
prompt_session = PromptSession()
|
@@ -452,13 +437,9 @@ async def handle_special_commands(command, agent_app=None):
|
|
452
437
|
rich_print(" /prompt <name> - Apply a specific prompt by name")
|
453
438
|
rich_print(" @agent_name - Switch to agent")
|
454
439
|
rich_print(" STOP - Return control back to the workflow")
|
455
|
-
rich_print(
|
456
|
-
" EXIT - Exit fast-agent, terminating any running workflows"
|
457
|
-
)
|
440
|
+
rich_print(" EXIT - Exit fast-agent, terminating any running workflows")
|
458
441
|
rich_print("\n[bold]Keyboard Shortcuts:[/bold]")
|
459
|
-
rich_print(
|
460
|
-
" Enter - Submit (normal mode) / New line (multiline mode)"
|
461
|
-
)
|
442
|
+
rich_print(" Enter - Submit (normal mode) / New line (multiline mode)")
|
462
443
|
rich_print(" Ctrl+Enter - Always submit (in any mode)")
|
463
444
|
rich_print(" Ctrl+T - Toggle multiline mode")
|
464
445
|
rich_print(" Ctrl+L - Clear input")
|
@@ -489,14 +470,10 @@ async def handle_special_commands(command, agent_app=None):
|
|
489
470
|
rich_print("\n[bold]Fetching available MCP prompts...[/bold]")
|
490
471
|
return {"list_prompts": True}
|
491
472
|
else:
|
492
|
-
rich_print(
|
493
|
-
"[yellow]Prompt listing is not available outside of an agent context[/yellow]"
|
494
|
-
)
|
473
|
+
rich_print("[yellow]Prompt listing is not available outside of an agent context[/yellow]")
|
495
474
|
return True
|
496
475
|
|
497
|
-
elif command == "SELECT_PROMPT" or (
|
498
|
-
isinstance(command, str) and command.startswith("SELECT_PROMPT:")
|
499
|
-
):
|
476
|
+
elif command == "SELECT_PROMPT" or (isinstance(command, str) and command.startswith("SELECT_PROMPT:")):
|
500
477
|
# Handle prompt selection UI
|
501
478
|
if agent_app:
|
502
479
|
# If it's a specific prompt, extract the name
|
@@ -507,9 +484,7 @@ async def handle_special_commands(command, agent_app=None):
|
|
507
484
|
# Return a dictionary with a select_prompt action to be handled by the caller
|
508
485
|
return {"select_prompt": True, "prompt_name": prompt_name}
|
509
486
|
else:
|
510
|
-
rich_print(
|
511
|
-
"[yellow]Prompt selection is not available outside of an agent context[/yellow]"
|
512
|
-
)
|
487
|
+
rich_print("[yellow]Prompt selection is not available outside of an agent context[/yellow]")
|
513
488
|
return True
|
514
489
|
|
515
490
|
elif isinstance(command, str) and command.startswith("SWITCH:"):
|
@@ -519,9 +494,7 @@ async def handle_special_commands(command, agent_app=None):
|
|
519
494
|
# rich_print(f"[green]Switching to agent: {agent_name}[/green]")
|
520
495
|
return {"switch_agent": agent_name}
|
521
496
|
else:
|
522
|
-
rich_print(
|
523
|
-
"[yellow]Agent switching not available in this context[/yellow]"
|
524
|
-
)
|
497
|
+
rich_print("[yellow]Agent switching not available in this context[/yellow]")
|
525
498
|
else:
|
526
499
|
rich_print(f"[red]Unknown agent: {agent_name}[/red]")
|
527
500
|
return True
|
mcp_agent/core/error_handling.py
CHANGED
mcp_agent/core/exceptions.py
CHANGED
@@ -7,7 +7,7 @@ Enables user-friendly error handling for common issues.
|
|
7
7
|
class FastAgentError(Exception):
|
8
8
|
"""Base exception class for FastAgent errors"""
|
9
9
|
|
10
|
-
def __init__(self, message: str, details: str = ""):
|
10
|
+
def __init__(self, message: str, details: str = "") -> None:
|
11
11
|
self.message = message
|
12
12
|
self.details = details
|
13
13
|
super().__init__(f"{message}\n\n{details}" if details else message)
|
@@ -18,7 +18,7 @@ class ServerConfigError(FastAgentError):
|
|
18
18
|
Example: Server name referenced in agent.servers[] but not defined in config
|
19
19
|
"""
|
20
20
|
|
21
|
-
def __init__(self, message: str, details: str = ""):
|
21
|
+
def __init__(self, message: str, details: str = "") -> None:
|
22
22
|
super().__init__(message, details)
|
23
23
|
|
24
24
|
|
@@ -27,7 +27,7 @@ class AgentConfigError(FastAgentError):
|
|
27
27
|
Example: Parallel fan-in references unknown agent
|
28
28
|
"""
|
29
29
|
|
30
|
-
def __init__(self, message: str, details: str = ""):
|
30
|
+
def __init__(self, message: str, details: str = "") -> None:
|
31
31
|
super().__init__(message, details)
|
32
32
|
|
33
33
|
|
@@ -36,14 +36,14 @@ class ProviderKeyError(FastAgentError):
|
|
36
36
|
Example: OpenAI/Anthropic key not configured but model requires it
|
37
37
|
"""
|
38
38
|
|
39
|
-
def __init__(self, message: str, details: str = ""):
|
39
|
+
def __init__(self, message: str, details: str = "") -> None:
|
40
40
|
super().__init__(message, details)
|
41
41
|
|
42
42
|
|
43
43
|
class ServerInitializationError(FastAgentError):
|
44
44
|
"""Raised when a server fails to initialize properly."""
|
45
45
|
|
46
|
-
def __init__(self, message: str, details: str = ""):
|
46
|
+
def __init__(self, message: str, details: str = "") -> None:
|
47
47
|
super().__init__(message, details)
|
48
48
|
|
49
49
|
|
@@ -52,14 +52,14 @@ class ModelConfigError(FastAgentError):
|
|
52
52
|
Example: Unknown model name in model specification string
|
53
53
|
"""
|
54
54
|
|
55
|
-
def __init__(self, message: str, details: str = ""):
|
55
|
+
def __init__(self, message: str, details: str = "") -> None:
|
56
56
|
super().__init__(message, details)
|
57
57
|
|
58
58
|
|
59
59
|
class CircularDependencyError(FastAgentError):
|
60
60
|
"""Raised when we detect a Circular Dependency in the workflow"""
|
61
61
|
|
62
|
-
def __init__(self, message: str, details: str = ""):
|
62
|
+
def __init__(self, message: str, details: str = "") -> None:
|
63
63
|
super().__init__(message, details)
|
64
64
|
|
65
65
|
|
@@ -67,5 +67,5 @@ class PromptExitError(FastAgentError):
|
|
67
67
|
"""Raised from enhanced_prompt when the user requests hard exits"""
|
68
68
|
|
69
69
|
# TODO an exception for flow control :(
|
70
|
-
def __init__(self, message: str, details: str = ""):
|
70
|
+
def __init__(self, message: str, details: str = "") -> None:
|
71
71
|
super().__init__(message, details)
|
mcp_agent/core/factory.py
CHANGED
@@ -2,10 +2,22 @@
|
|
2
2
|
Factory functions for creating agent and workflow instances.
|
3
3
|
"""
|
4
4
|
|
5
|
-
from typing import
|
5
|
+
from typing import Any, Callable, Dict, Optional, TypeVar
|
6
6
|
|
7
|
+
from mcp_agent.agents.agent import Agent
|
7
8
|
from mcp_agent.app import MCPApp
|
8
|
-
from mcp_agent.
|
9
|
+
from mcp_agent.core.agent_types import AgentConfig, AgentType
|
10
|
+
from mcp_agent.core.agent_utils import get_agent_instances, log_agent_load, unwrap_proxy
|
11
|
+
from mcp_agent.core.exceptions import AgentConfigError
|
12
|
+
from mcp_agent.core.proxies import (
|
13
|
+
BaseAgentProxy,
|
14
|
+
ChainProxy,
|
15
|
+
LLMAgentProxy,
|
16
|
+
RouterProxy,
|
17
|
+
WorkflowProxy,
|
18
|
+
)
|
19
|
+
from mcp_agent.core.types import AgentOrWorkflow, ProxyDict
|
20
|
+
from mcp_agent.core.validation import get_dependencies
|
9
21
|
from mcp_agent.event_progress import ProgressAction
|
10
22
|
from mcp_agent.workflows.evaluator_optimizer.evaluator_optimizer import (
|
11
23
|
EvaluatorOptimizerLLM,
|
@@ -17,25 +29,10 @@ from mcp_agent.workflows.orchestrator.orchestrator import Orchestrator
|
|
17
29
|
from mcp_agent.workflows.parallel.parallel_llm import ParallelLLM
|
18
30
|
from mcp_agent.workflows.router.router_llm import LLMRouter
|
19
31
|
|
20
|
-
from mcp_agent.core.agent_types import AgentType
|
21
|
-
from mcp_agent.core.exceptions import AgentConfigError
|
22
|
-
from mcp_agent.core.proxies import (
|
23
|
-
BaseAgentProxy,
|
24
|
-
LLMAgentProxy,
|
25
|
-
WorkflowProxy,
|
26
|
-
RouterProxy,
|
27
|
-
ChainProxy,
|
28
|
-
)
|
29
|
-
from mcp_agent.core.types import AgentOrWorkflow, ProxyDict
|
30
|
-
from mcp_agent.core.agent_utils import log_agent_load, unwrap_proxy, get_agent_instances
|
31
|
-
from mcp_agent.core.validation import get_dependencies
|
32
|
-
|
33
32
|
T = TypeVar("T") # For the wrapper classes
|
34
33
|
|
35
34
|
|
36
|
-
def create_proxy(
|
37
|
-
app: MCPApp, name: str, instance: AgentOrWorkflow, agent_type: str
|
38
|
-
) -> BaseAgentProxy:
|
35
|
+
def create_proxy(app: MCPApp, name: str, instance: AgentOrWorkflow, agent_type: str) -> BaseAgentProxy:
|
39
36
|
"""Create appropriate proxy type based on agent type and validate instance type
|
40
37
|
|
41
38
|
Args:
|
@@ -62,27 +59,19 @@ def create_proxy(
|
|
62
59
|
return LLMAgentProxy(app, name, instance)
|
63
60
|
elif agent_type == AgentType.ORCHESTRATOR.value:
|
64
61
|
if not isinstance(instance, Orchestrator):
|
65
|
-
raise TypeError(
|
66
|
-
f"Expected Orchestrator instance for {name}, got {type(instance)}"
|
67
|
-
)
|
62
|
+
raise TypeError(f"Expected Orchestrator instance for {name}, got {type(instance)}")
|
68
63
|
return WorkflowProxy(app, name, instance)
|
69
64
|
elif agent_type == AgentType.PARALLEL.value:
|
70
65
|
if not isinstance(instance, ParallelLLM):
|
71
|
-
raise TypeError(
|
72
|
-
f"Expected ParallelLLM instance for {name}, got {type(instance)}"
|
73
|
-
)
|
66
|
+
raise TypeError(f"Expected ParallelLLM instance for {name}, got {type(instance)}")
|
74
67
|
return WorkflowProxy(app, name, instance)
|
75
68
|
elif agent_type == AgentType.EVALUATOR_OPTIMIZER.value:
|
76
69
|
if not isinstance(instance, EvaluatorOptimizerLLM):
|
77
|
-
raise TypeError(
|
78
|
-
f"Expected EvaluatorOptimizerLLM instance for {name}, got {type(instance)}"
|
79
|
-
)
|
70
|
+
raise TypeError(f"Expected EvaluatorOptimizerLLM instance for {name}, got {type(instance)}")
|
80
71
|
return WorkflowProxy(app, name, instance)
|
81
72
|
elif agent_type == AgentType.ROUTER.value:
|
82
73
|
if not isinstance(instance, LLMRouter):
|
83
|
-
raise TypeError(
|
84
|
-
f"Expected LLMRouter instance for {name}, got {type(instance)}"
|
85
|
-
)
|
74
|
+
raise TypeError(f"Expected LLMRouter instance for {name}, got {type(instance)}")
|
86
75
|
return RouterProxy(app, name, instance)
|
87
76
|
elif agent_type == AgentType.CHAIN.value:
|
88
77
|
# Chain proxy is directly returned from _create_agents_by_type
|
@@ -188,11 +177,7 @@ async def create_agents_by_type(
|
|
188
177
|
|
189
178
|
elif agent_type == AgentType.ORCHESTRATOR:
|
190
179
|
# Get base params configured with model settings
|
191
|
-
base_params = (
|
192
|
-
config.default_request_params.model_copy()
|
193
|
-
if config.default_request_params
|
194
|
-
else RequestParams()
|
195
|
-
)
|
180
|
+
base_params = config.default_request_params.model_copy() if config.default_request_params else RequestParams()
|
196
181
|
base_params.use_history = False # Force no history for orchestrator
|
197
182
|
|
198
183
|
# Get the child agents - need to unwrap proxies and validate LLM config
|
@@ -232,7 +217,6 @@ async def create_agents_by_type(
|
|
232
217
|
)
|
233
218
|
|
234
219
|
planner = await planner_agent.attach_llm(planner_factory)
|
235
|
-
await planner.initialize()
|
236
220
|
# Create the orchestrator with pre-configured planner
|
237
221
|
instance = Orchestrator(
|
238
222
|
name=config.name,
|
@@ -240,9 +224,7 @@ async def create_agents_by_type(
|
|
240
224
|
available_agents=child_agents,
|
241
225
|
context=app_instance.context,
|
242
226
|
request_params=planner.default_request_params, # Base params already include model settings
|
243
|
-
plan_type=agent_data.get(
|
244
|
-
"plan_type", "full"
|
245
|
-
), # Get plan_type from agent_data
|
227
|
+
plan_type=agent_data.get("plan_type", "full"), # Get plan_type from agent_data
|
246
228
|
verb=ProgressAction.PLANNING,
|
247
229
|
)
|
248
230
|
|
@@ -252,19 +234,13 @@ async def create_agents_by_type(
|
|
252
234
|
evaluator = unwrap_proxy(active_agents[agent_data["evaluator"]])
|
253
235
|
|
254
236
|
if not generator or not evaluator:
|
255
|
-
raise ValueError(
|
256
|
-
f"Missing agents for workflow {name}: "
|
257
|
-
f"generator={agent_data['generator']}, "
|
258
|
-
f"evaluator={agent_data['evaluator']}"
|
259
|
-
)
|
237
|
+
raise ValueError(f"Missing agents for workflow {name}: generator={agent_data['generator']}, evaluator={agent_data['evaluator']}")
|
260
238
|
|
261
239
|
# Get model from generator if it's an Agent, or from config otherwise
|
262
240
|
optimizer_model = None
|
263
241
|
if isinstance(generator, Agent):
|
264
242
|
optimizer_model = generator.config.model
|
265
|
-
elif hasattr(generator, "_sequence") and hasattr(
|
266
|
-
generator, "_agent_proxies"
|
267
|
-
):
|
243
|
+
elif hasattr(generator, "_sequence") and hasattr(generator, "_agent_proxies"):
|
268
244
|
# For ChainProxy, use the config model directly
|
269
245
|
optimizer_model = config.model
|
270
246
|
|
@@ -329,27 +305,19 @@ async def create_agents_by_type(
|
|
329
305
|
|
330
306
|
# Generate a better description
|
331
307
|
if agent_names:
|
332
|
-
server_part = (
|
333
|
-
f" with access to servers: {', '.join(sorted(all_servers))}"
|
334
|
-
if all_servers
|
335
|
-
else ""
|
336
|
-
)
|
308
|
+
server_part = f" with access to servers: {', '.join(sorted(all_servers))}" if all_servers else ""
|
337
309
|
config.instruction = f"Sequence of agents: {', '.join(agent_names)}{server_part}."
|
338
310
|
|
339
311
|
# Create a ChainProxy without needing a new instance
|
340
312
|
# Just pass the agent proxies and sequence
|
341
313
|
instance = ChainProxy(app_instance, name, sequence, active_agents)
|
342
314
|
# Set continue_with_final behavior from configuration
|
343
|
-
instance._continue_with_final = agent_data.get(
|
344
|
-
"continue_with_final", True
|
345
|
-
)
|
315
|
+
instance._continue_with_final = agent_data.get("continue_with_final", True)
|
346
316
|
# Set cumulative behavior from configuration
|
347
317
|
instance._cumulative = agent_data.get("cumulative", False)
|
348
318
|
|
349
319
|
elif agent_type == AgentType.PARALLEL:
|
350
|
-
fan_out_agents = get_agent_instances(
|
351
|
-
agent_data["fan_out"], active_agents
|
352
|
-
)
|
320
|
+
fan_out_agents = get_agent_instances(agent_data["fan_out"], active_agents)
|
353
321
|
|
354
322
|
# Get fan-in agent - unwrap proxy
|
355
323
|
fan_in_agent = unwrap_proxy(active_agents[agent_data["fan_in"]])
|
@@ -371,9 +339,7 @@ async def create_agents_by_type(
|
|
371
339
|
raise ValueError(f"Unsupported agent type: {agent_type}")
|
372
340
|
|
373
341
|
# Create the appropriate proxy and store in results
|
374
|
-
result_agents[name] = create_proxy(
|
375
|
-
app_instance, name, instance, agent_type.value
|
376
|
-
)
|
342
|
+
result_agents[name] = create_proxy(app_instance, name, instance, agent_type.value)
|
377
343
|
|
378
344
|
return result_agents
|
379
345
|
|
@@ -427,24 +393,16 @@ async def create_agents_in_dependency_order(
|
|
427
393
|
visited = set()
|
428
394
|
|
429
395
|
# Get all agents of the specified type
|
430
|
-
agent_names = [
|
431
|
-
name
|
432
|
-
for name, agent_data in agents_dict.items()
|
433
|
-
if agent_data["type"] == agent_type.value
|
434
|
-
]
|
396
|
+
agent_names = [name for name, agent_data in agents_dict.items() if agent_data["type"] == agent_type.value]
|
435
397
|
|
436
398
|
# Create agents in dependency order
|
437
399
|
for name in agent_names:
|
438
400
|
# Get ordered dependencies if not already processed
|
439
401
|
if name not in visited:
|
440
402
|
try:
|
441
|
-
ordered_agents = get_dependencies(
|
442
|
-
name, agents_dict, visited, set(), agent_type
|
443
|
-
)
|
403
|
+
ordered_agents = get_dependencies(name, agents_dict, visited, set(), agent_type)
|
444
404
|
except ValueError as e:
|
445
|
-
raise ValueError(
|
446
|
-
f"Error creating {agent_type.name.lower()} agent {name}: {str(e)}"
|
447
|
-
)
|
405
|
+
raise ValueError(f"Error creating {agent_type.name.lower()} agent {name}: {str(e)}")
|
448
406
|
|
449
407
|
# Create each agent in order
|
450
408
|
for agent_name in ordered_agents:
|