fast-agent-mcp 0.4.7__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/__init__.py +183 -0
- fast_agent/acp/__init__.py +19 -0
- fast_agent/acp/acp_aware_mixin.py +304 -0
- fast_agent/acp/acp_context.py +437 -0
- fast_agent/acp/content_conversion.py +136 -0
- fast_agent/acp/filesystem_runtime.py +427 -0
- fast_agent/acp/permission_store.py +269 -0
- fast_agent/acp/server/__init__.py +5 -0
- fast_agent/acp/server/agent_acp_server.py +1472 -0
- fast_agent/acp/slash_commands.py +1050 -0
- fast_agent/acp/terminal_runtime.py +408 -0
- fast_agent/acp/tool_permission_adapter.py +125 -0
- fast_agent/acp/tool_permissions.py +474 -0
- fast_agent/acp/tool_progress.py +814 -0
- fast_agent/agents/__init__.py +85 -0
- fast_agent/agents/agent_types.py +64 -0
- fast_agent/agents/llm_agent.py +350 -0
- fast_agent/agents/llm_decorator.py +1139 -0
- fast_agent/agents/mcp_agent.py +1337 -0
- fast_agent/agents/tool_agent.py +271 -0
- fast_agent/agents/workflow/agents_as_tools_agent.py +849 -0
- fast_agent/agents/workflow/chain_agent.py +212 -0
- fast_agent/agents/workflow/evaluator_optimizer.py +380 -0
- fast_agent/agents/workflow/iterative_planner.py +652 -0
- fast_agent/agents/workflow/maker_agent.py +379 -0
- fast_agent/agents/workflow/orchestrator_models.py +218 -0
- fast_agent/agents/workflow/orchestrator_prompts.py +248 -0
- fast_agent/agents/workflow/parallel_agent.py +250 -0
- fast_agent/agents/workflow/router_agent.py +353 -0
- fast_agent/cli/__init__.py +0 -0
- fast_agent/cli/__main__.py +73 -0
- fast_agent/cli/commands/acp.py +159 -0
- fast_agent/cli/commands/auth.py +404 -0
- fast_agent/cli/commands/check_config.py +783 -0
- fast_agent/cli/commands/go.py +514 -0
- fast_agent/cli/commands/quickstart.py +557 -0
- fast_agent/cli/commands/serve.py +143 -0
- fast_agent/cli/commands/server_helpers.py +114 -0
- fast_agent/cli/commands/setup.py +174 -0
- fast_agent/cli/commands/url_parser.py +190 -0
- fast_agent/cli/constants.py +40 -0
- fast_agent/cli/main.py +115 -0
- fast_agent/cli/terminal.py +24 -0
- fast_agent/config.py +798 -0
- fast_agent/constants.py +41 -0
- fast_agent/context.py +279 -0
- fast_agent/context_dependent.py +50 -0
- fast_agent/core/__init__.py +92 -0
- fast_agent/core/agent_app.py +448 -0
- fast_agent/core/core_app.py +137 -0
- fast_agent/core/direct_decorators.py +784 -0
- fast_agent/core/direct_factory.py +620 -0
- fast_agent/core/error_handling.py +27 -0
- fast_agent/core/exceptions.py +90 -0
- fast_agent/core/executor/__init__.py +0 -0
- fast_agent/core/executor/executor.py +280 -0
- fast_agent/core/executor/task_registry.py +32 -0
- fast_agent/core/executor/workflow_signal.py +324 -0
- fast_agent/core/fastagent.py +1186 -0
- fast_agent/core/logging/__init__.py +5 -0
- fast_agent/core/logging/events.py +138 -0
- fast_agent/core/logging/json_serializer.py +164 -0
- fast_agent/core/logging/listeners.py +309 -0
- fast_agent/core/logging/logger.py +278 -0
- fast_agent/core/logging/transport.py +481 -0
- fast_agent/core/prompt.py +9 -0
- fast_agent/core/prompt_templates.py +183 -0
- fast_agent/core/validation.py +326 -0
- fast_agent/event_progress.py +62 -0
- fast_agent/history/history_exporter.py +49 -0
- fast_agent/human_input/__init__.py +47 -0
- fast_agent/human_input/elicitation_handler.py +123 -0
- fast_agent/human_input/elicitation_state.py +33 -0
- fast_agent/human_input/form_elements.py +59 -0
- fast_agent/human_input/form_fields.py +256 -0
- fast_agent/human_input/simple_form.py +113 -0
- fast_agent/human_input/types.py +40 -0
- fast_agent/interfaces.py +310 -0
- fast_agent/llm/__init__.py +9 -0
- fast_agent/llm/cancellation.py +22 -0
- fast_agent/llm/fastagent_llm.py +931 -0
- fast_agent/llm/internal/passthrough.py +161 -0
- fast_agent/llm/internal/playback.py +129 -0
- fast_agent/llm/internal/silent.py +41 -0
- fast_agent/llm/internal/slow.py +38 -0
- fast_agent/llm/memory.py +275 -0
- fast_agent/llm/model_database.py +490 -0
- fast_agent/llm/model_factory.py +388 -0
- fast_agent/llm/model_info.py +102 -0
- fast_agent/llm/prompt_utils.py +155 -0
- fast_agent/llm/provider/anthropic/anthropic_utils.py +84 -0
- fast_agent/llm/provider/anthropic/cache_planner.py +56 -0
- fast_agent/llm/provider/anthropic/llm_anthropic.py +796 -0
- fast_agent/llm/provider/anthropic/multipart_converter_anthropic.py +462 -0
- fast_agent/llm/provider/bedrock/bedrock_utils.py +218 -0
- fast_agent/llm/provider/bedrock/llm_bedrock.py +2207 -0
- fast_agent/llm/provider/bedrock/multipart_converter_bedrock.py +84 -0
- fast_agent/llm/provider/google/google_converter.py +466 -0
- fast_agent/llm/provider/google/llm_google_native.py +681 -0
- fast_agent/llm/provider/openai/llm_aliyun.py +31 -0
- fast_agent/llm/provider/openai/llm_azure.py +143 -0
- fast_agent/llm/provider/openai/llm_deepseek.py +76 -0
- fast_agent/llm/provider/openai/llm_generic.py +35 -0
- fast_agent/llm/provider/openai/llm_google_oai.py +32 -0
- fast_agent/llm/provider/openai/llm_groq.py +42 -0
- fast_agent/llm/provider/openai/llm_huggingface.py +85 -0
- fast_agent/llm/provider/openai/llm_openai.py +1195 -0
- fast_agent/llm/provider/openai/llm_openai_compatible.py +138 -0
- fast_agent/llm/provider/openai/llm_openrouter.py +45 -0
- fast_agent/llm/provider/openai/llm_tensorzero_openai.py +128 -0
- fast_agent/llm/provider/openai/llm_xai.py +38 -0
- fast_agent/llm/provider/openai/multipart_converter_openai.py +561 -0
- fast_agent/llm/provider/openai/openai_multipart.py +169 -0
- fast_agent/llm/provider/openai/openai_utils.py +67 -0
- fast_agent/llm/provider/openai/responses.py +133 -0
- fast_agent/llm/provider_key_manager.py +139 -0
- fast_agent/llm/provider_types.py +34 -0
- fast_agent/llm/request_params.py +61 -0
- fast_agent/llm/sampling_converter.py +98 -0
- fast_agent/llm/stream_types.py +9 -0
- fast_agent/llm/usage_tracking.py +445 -0
- fast_agent/mcp/__init__.py +56 -0
- fast_agent/mcp/common.py +26 -0
- fast_agent/mcp/elicitation_factory.py +84 -0
- fast_agent/mcp/elicitation_handlers.py +164 -0
- fast_agent/mcp/gen_client.py +83 -0
- fast_agent/mcp/helpers/__init__.py +36 -0
- fast_agent/mcp/helpers/content_helpers.py +352 -0
- fast_agent/mcp/helpers/server_config_helpers.py +25 -0
- fast_agent/mcp/hf_auth.py +147 -0
- fast_agent/mcp/interfaces.py +92 -0
- fast_agent/mcp/logger_textio.py +108 -0
- fast_agent/mcp/mcp_agent_client_session.py +411 -0
- fast_agent/mcp/mcp_aggregator.py +2175 -0
- fast_agent/mcp/mcp_connection_manager.py +723 -0
- fast_agent/mcp/mcp_content.py +262 -0
- fast_agent/mcp/mime_utils.py +108 -0
- fast_agent/mcp/oauth_client.py +509 -0
- fast_agent/mcp/prompt.py +159 -0
- fast_agent/mcp/prompt_message_extended.py +155 -0
- fast_agent/mcp/prompt_render.py +84 -0
- fast_agent/mcp/prompt_serialization.py +580 -0
- fast_agent/mcp/prompts/__init__.py +0 -0
- fast_agent/mcp/prompts/__main__.py +7 -0
- fast_agent/mcp/prompts/prompt_constants.py +18 -0
- fast_agent/mcp/prompts/prompt_helpers.py +238 -0
- fast_agent/mcp/prompts/prompt_load.py +186 -0
- fast_agent/mcp/prompts/prompt_server.py +552 -0
- fast_agent/mcp/prompts/prompt_template.py +438 -0
- fast_agent/mcp/resource_utils.py +215 -0
- fast_agent/mcp/sampling.py +200 -0
- fast_agent/mcp/server/__init__.py +4 -0
- fast_agent/mcp/server/agent_server.py +613 -0
- fast_agent/mcp/skybridge.py +44 -0
- fast_agent/mcp/sse_tracking.py +287 -0
- fast_agent/mcp/stdio_tracking_simple.py +59 -0
- fast_agent/mcp/streamable_http_tracking.py +309 -0
- fast_agent/mcp/tool_execution_handler.py +137 -0
- fast_agent/mcp/tool_permission_handler.py +88 -0
- fast_agent/mcp/transport_tracking.py +634 -0
- fast_agent/mcp/types.py +24 -0
- fast_agent/mcp/ui_agent.py +48 -0
- fast_agent/mcp/ui_mixin.py +209 -0
- fast_agent/mcp_server_registry.py +89 -0
- fast_agent/py.typed +0 -0
- fast_agent/resources/examples/data-analysis/analysis-campaign.py +189 -0
- fast_agent/resources/examples/data-analysis/analysis.py +68 -0
- fast_agent/resources/examples/data-analysis/fastagent.config.yaml +41 -0
- fast_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +1471 -0
- fast_agent/resources/examples/mcp/elicitations/elicitation_account_server.py +88 -0
- fast_agent/resources/examples/mcp/elicitations/elicitation_forms_server.py +297 -0
- fast_agent/resources/examples/mcp/elicitations/elicitation_game_server.py +164 -0
- fast_agent/resources/examples/mcp/elicitations/fastagent.config.yaml +35 -0
- fast_agent/resources/examples/mcp/elicitations/fastagent.secrets.yaml.example +17 -0
- fast_agent/resources/examples/mcp/elicitations/forms_demo.py +107 -0
- fast_agent/resources/examples/mcp/elicitations/game_character.py +65 -0
- fast_agent/resources/examples/mcp/elicitations/game_character_handler.py +256 -0
- fast_agent/resources/examples/mcp/elicitations/tool_call.py +21 -0
- fast_agent/resources/examples/mcp/state-transfer/agent_one.py +18 -0
- fast_agent/resources/examples/mcp/state-transfer/agent_two.py +18 -0
- fast_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml +27 -0
- fast_agent/resources/examples/mcp/state-transfer/fastagent.secrets.yaml.example +15 -0
- fast_agent/resources/examples/researcher/fastagent.config.yaml +61 -0
- fast_agent/resources/examples/researcher/researcher-eval.py +53 -0
- fast_agent/resources/examples/researcher/researcher-imp.py +189 -0
- fast_agent/resources/examples/researcher/researcher.py +36 -0
- fast_agent/resources/examples/tensorzero/.env.sample +2 -0
- fast_agent/resources/examples/tensorzero/Makefile +31 -0
- fast_agent/resources/examples/tensorzero/README.md +56 -0
- fast_agent/resources/examples/tensorzero/agent.py +35 -0
- fast_agent/resources/examples/tensorzero/demo_images/clam.jpg +0 -0
- fast_agent/resources/examples/tensorzero/demo_images/crab.png +0 -0
- fast_agent/resources/examples/tensorzero/demo_images/shrimp.png +0 -0
- fast_agent/resources/examples/tensorzero/docker-compose.yml +105 -0
- fast_agent/resources/examples/tensorzero/fastagent.config.yaml +19 -0
- fast_agent/resources/examples/tensorzero/image_demo.py +67 -0
- fast_agent/resources/examples/tensorzero/mcp_server/Dockerfile +25 -0
- fast_agent/resources/examples/tensorzero/mcp_server/entrypoint.sh +35 -0
- fast_agent/resources/examples/tensorzero/mcp_server/mcp_server.py +31 -0
- fast_agent/resources/examples/tensorzero/mcp_server/pyproject.toml +11 -0
- fast_agent/resources/examples/tensorzero/simple_agent.py +25 -0
- fast_agent/resources/examples/tensorzero/tensorzero_config/system_schema.json +29 -0
- fast_agent/resources/examples/tensorzero/tensorzero_config/system_template.minijinja +11 -0
- fast_agent/resources/examples/tensorzero/tensorzero_config/tensorzero.toml +35 -0
- fast_agent/resources/examples/workflows/agents_as_tools_extended.py +73 -0
- fast_agent/resources/examples/workflows/agents_as_tools_simple.py +50 -0
- fast_agent/resources/examples/workflows/chaining.py +37 -0
- fast_agent/resources/examples/workflows/evaluator.py +77 -0
- fast_agent/resources/examples/workflows/fastagent.config.yaml +26 -0
- fast_agent/resources/examples/workflows/graded_report.md +89 -0
- fast_agent/resources/examples/workflows/human_input.py +28 -0
- fast_agent/resources/examples/workflows/maker.py +156 -0
- fast_agent/resources/examples/workflows/orchestrator.py +70 -0
- fast_agent/resources/examples/workflows/parallel.py +56 -0
- fast_agent/resources/examples/workflows/router.py +69 -0
- fast_agent/resources/examples/workflows/short_story.md +13 -0
- fast_agent/resources/examples/workflows/short_story.txt +19 -0
- fast_agent/resources/setup/.gitignore +30 -0
- fast_agent/resources/setup/agent.py +28 -0
- fast_agent/resources/setup/fastagent.config.yaml +65 -0
- fast_agent/resources/setup/fastagent.secrets.yaml.example +38 -0
- fast_agent/resources/setup/pyproject.toml.tmpl +23 -0
- fast_agent/skills/__init__.py +9 -0
- fast_agent/skills/registry.py +235 -0
- fast_agent/tools/elicitation.py +369 -0
- fast_agent/tools/shell_runtime.py +402 -0
- fast_agent/types/__init__.py +59 -0
- fast_agent/types/conversation_summary.py +294 -0
- fast_agent/types/llm_stop_reason.py +78 -0
- fast_agent/types/message_search.py +249 -0
- fast_agent/ui/__init__.py +38 -0
- fast_agent/ui/console.py +59 -0
- fast_agent/ui/console_display.py +1080 -0
- fast_agent/ui/elicitation_form.py +946 -0
- fast_agent/ui/elicitation_style.py +59 -0
- fast_agent/ui/enhanced_prompt.py +1400 -0
- fast_agent/ui/history_display.py +734 -0
- fast_agent/ui/interactive_prompt.py +1199 -0
- fast_agent/ui/markdown_helpers.py +104 -0
- fast_agent/ui/markdown_truncator.py +1004 -0
- fast_agent/ui/mcp_display.py +857 -0
- fast_agent/ui/mcp_ui_utils.py +235 -0
- fast_agent/ui/mermaid_utils.py +169 -0
- fast_agent/ui/message_primitives.py +50 -0
- fast_agent/ui/notification_tracker.py +205 -0
- fast_agent/ui/plain_text_truncator.py +68 -0
- fast_agent/ui/progress_display.py +10 -0
- fast_agent/ui/rich_progress.py +195 -0
- fast_agent/ui/streaming.py +774 -0
- fast_agent/ui/streaming_buffer.py +449 -0
- fast_agent/ui/tool_display.py +422 -0
- fast_agent/ui/usage_display.py +204 -0
- fast_agent/utils/__init__.py +5 -0
- fast_agent/utils/reasoning_stream_parser.py +77 -0
- fast_agent/utils/time.py +22 -0
- fast_agent/workflow_telemetry.py +261 -0
- fast_agent_mcp-0.4.7.dist-info/METADATA +788 -0
- fast_agent_mcp-0.4.7.dist-info/RECORD +261 -0
- fast_agent_mcp-0.4.7.dist-info/WHEEL +4 -0
- fast_agent_mcp-0.4.7.dist-info/entry_points.txt +7 -0
- fast_agent_mcp-0.4.7.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Prompt templates used by the Orchestrator workflow.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
# Templates for formatting results
|
|
6
|
+
TASK_RESULT_TEMPLATE = """Task: {task_description}
|
|
7
|
+
Result: {task_result}"""
|
|
8
|
+
|
|
9
|
+
STEP_RESULT_TEMPLATE = """Step: {step_description}
|
|
10
|
+
Step Subtasks:
|
|
11
|
+
{tasks_str}"""
|
|
12
|
+
|
|
13
|
+
PLAN_RESULT_TEMPLATE = """Plan Objective: {plan_objective}
|
|
14
|
+
|
|
15
|
+
Progress So Far (steps completed):
|
|
16
|
+
{steps_str}
|
|
17
|
+
|
|
18
|
+
Result: {plan_result}"""
|
|
19
|
+
|
|
20
|
+
FULL_PLAN_PROMPT_TEMPLATE = """You are tasked with orchestrating a plan to complete an objective.
|
|
21
|
+
You can analyze results from the previous steps already executed to decide if the objective is complete.
|
|
22
|
+
|
|
23
|
+
<fastagent:data>
|
|
24
|
+
<fastagent:objective>
|
|
25
|
+
{objective}
|
|
26
|
+
</fastagent:objective>
|
|
27
|
+
|
|
28
|
+
<fastagent:available-agents>
|
|
29
|
+
{agents}
|
|
30
|
+
</fastagent:available-agents>
|
|
31
|
+
|
|
32
|
+
<fastagent:progress>
|
|
33
|
+
{plan_result}
|
|
34
|
+
</fastagent:progress>
|
|
35
|
+
|
|
36
|
+
<fastagent:status>
|
|
37
|
+
{plan_status}
|
|
38
|
+
{iterations_info}
|
|
39
|
+
</fastagent:status>
|
|
40
|
+
</fastagent:data>
|
|
41
|
+
|
|
42
|
+
Your plan must be structured in sequential steps, with each step containing independent parallel subtasks.
|
|
43
|
+
If the previous results achieve the objective, return is_complete=True.
|
|
44
|
+
Otherwise, generate remaining steps needed.
|
|
45
|
+
|
|
46
|
+
<fastagent:instruction>
|
|
47
|
+
You are operating in "full plan" mode, where you generate a complete plan with ALL remaining steps needed.
|
|
48
|
+
After receiving your plan, the system will execute ALL steps in your plan before asking for your input again.
|
|
49
|
+
If the plan needs multiple iterations, you'll be called again with updated results.
|
|
50
|
+
|
|
51
|
+
Generate a plan with all remaining steps needed.
|
|
52
|
+
Steps are sequential, but each Step can have parallel subtasks.
|
|
53
|
+
For each Step, specify a description of the step and independent subtasks that can run in parallel.
|
|
54
|
+
For each subtask specify:
|
|
55
|
+
1. Clear description of the task that an LLM can execute
|
|
56
|
+
2. Name of 1 Agent from the available agents list above
|
|
57
|
+
|
|
58
|
+
CRITICAL: You MUST ONLY use agent names that are EXACTLY as they appear in <fastagent:available-agents> above.
|
|
59
|
+
Do NOT invent new agents. Do NOT modify agent names. The plan will FAIL if you use an agent that doesn't exist.
|
|
60
|
+
|
|
61
|
+
Return your response in the following JSON structure:
|
|
62
|
+
{{
|
|
63
|
+
"steps": [
|
|
64
|
+
{{
|
|
65
|
+
"description": "Description of step 1",
|
|
66
|
+
"tasks": [
|
|
67
|
+
{{
|
|
68
|
+
"description": "Description of task 1",
|
|
69
|
+
"agent": "agent_name" // agent MUST be exactly one of the agent names listed above
|
|
70
|
+
}},
|
|
71
|
+
{{
|
|
72
|
+
"description": "Description of task 2",
|
|
73
|
+
"agent": "agent_name2" // agent MUST be exactly one of the agent names listed above
|
|
74
|
+
}}
|
|
75
|
+
]
|
|
76
|
+
}}
|
|
77
|
+
],
|
|
78
|
+
"is_complete": false
|
|
79
|
+
}}
|
|
80
|
+
|
|
81
|
+
Set "is_complete" to true when ANY of these conditions are met:
|
|
82
|
+
1. The objective has been achieved in full or substantively
|
|
83
|
+
2. The remaining work is minor or trivial compared to what's been accomplished
|
|
84
|
+
3. Additional steps provide minimal value toward the core objective
|
|
85
|
+
4. The plan has gathered sufficient information to answer the original request
|
|
86
|
+
|
|
87
|
+
Be decisive - avoid excessive planning steps that add little value. It's better to complete a plan early than to continue with marginal improvements. Focus on the core intent of the objective, not perfection.
|
|
88
|
+
|
|
89
|
+
You must respond with valid JSON only, with no triple backticks. No markdown formatting.
|
|
90
|
+
No extra text. Do not wrap in ```json code fences.
|
|
91
|
+
</fastagent:instruction>
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
ITERATIVE_PLAN_PROMPT_TEMPLATE = """You are tasked with determining only the next step in a plan
|
|
95
|
+
needed to complete an objective. You must analyze the current state and progress from previous steps
|
|
96
|
+
to decide what to do next.
|
|
97
|
+
|
|
98
|
+
<fastagent:data>
|
|
99
|
+
<fastagent:objective>
|
|
100
|
+
{objective}
|
|
101
|
+
</fastagent:objective>
|
|
102
|
+
|
|
103
|
+
<fastagent:available-agents>
|
|
104
|
+
{agents}
|
|
105
|
+
</fastagent:available-agents>
|
|
106
|
+
|
|
107
|
+
<fastagent:progress>
|
|
108
|
+
{plan_result}
|
|
109
|
+
</fastagent:progress>
|
|
110
|
+
|
|
111
|
+
<fastagent:status>
|
|
112
|
+
{plan_status}
|
|
113
|
+
{iterations_info}
|
|
114
|
+
</fastagent:status>
|
|
115
|
+
</fastagent:data>
|
|
116
|
+
|
|
117
|
+
A Step must be sequential in the plan, but can have independent parallel subtasks. Only return a single Step.
|
|
118
|
+
If the previous results achieve the objective, return is_complete=True.
|
|
119
|
+
Otherwise, generate the next Step.
|
|
120
|
+
|
|
121
|
+
<fastagent:instruction>
|
|
122
|
+
You are operating in "iterative plan" mode, where you generate ONLY ONE STEP at a time.
|
|
123
|
+
After each step is executed, you'll be called again to determine the next step based on updated results.
|
|
124
|
+
|
|
125
|
+
Generate the next step, by specifying a description of the step and independent subtasks that can run in parallel:
|
|
126
|
+
For each subtask specify:
|
|
127
|
+
1. Clear description of the task that an LLM can execute
|
|
128
|
+
2. Name of 1 Agent from the available agents list above
|
|
129
|
+
|
|
130
|
+
CRITICAL: You MUST ONLY use agent names that are EXACTLY as they appear in <fastagent:available-agents> above.
|
|
131
|
+
Do NOT invent new agents. Do NOT modify agent names. The plan will FAIL if you use an agent that doesn't exist.
|
|
132
|
+
|
|
133
|
+
Return your response in the following JSON structure:
|
|
134
|
+
{{
|
|
135
|
+
"description": "Description of step 1",
|
|
136
|
+
"tasks": [
|
|
137
|
+
{{
|
|
138
|
+
"description": "Description of task 1",
|
|
139
|
+
"agent": "agent_name" // agent MUST be exactly one of the agent names listed above
|
|
140
|
+
}}
|
|
141
|
+
],
|
|
142
|
+
"is_complete": false
|
|
143
|
+
}}
|
|
144
|
+
|
|
145
|
+
Set "is_complete" to true when ANY of these conditions are met:
|
|
146
|
+
1. The objective has been achieved in full or substantively
|
|
147
|
+
2. The remaining work is minor or trivial compared to what's been accomplished
|
|
148
|
+
3. Additional steps provide minimal value toward the core objective
|
|
149
|
+
4. The plan has gathered sufficient information to answer the original request
|
|
150
|
+
|
|
151
|
+
Be decisive - avoid excessive planning steps that add little value. It's better to complete a plan early than to continue with marginal improvements. Focus on the core intent of the objective, not perfection.
|
|
152
|
+
|
|
153
|
+
You must respond with valid JSON only, with no triple backticks. No markdown formatting.
|
|
154
|
+
No extra text. Do not wrap in ```json code fences.
|
|
155
|
+
</fastagent:instruction>
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
TASK_PROMPT_TEMPLATE = """You are part of a larger workflow to achieve an objective.
|
|
159
|
+
|
|
160
|
+
<fastagent:data>
|
|
161
|
+
<fastagent:objective>
|
|
162
|
+
{objective}
|
|
163
|
+
</fastagent:objective>
|
|
164
|
+
|
|
165
|
+
<fastagent:task>
|
|
166
|
+
{task}
|
|
167
|
+
</fastagent:task>
|
|
168
|
+
|
|
169
|
+
<fastagent:context>
|
|
170
|
+
{context}
|
|
171
|
+
</fastagent:context>
|
|
172
|
+
</fastagent:data>
|
|
173
|
+
|
|
174
|
+
<fastagent:instruction>
|
|
175
|
+
Your job is to accomplish only the task specified above.
|
|
176
|
+
Use the context from previous steps to inform your approach.
|
|
177
|
+
The context contains structured XML with the results from previous steps - pay close attention to:
|
|
178
|
+
- The objective in <fastagent:objective>
|
|
179
|
+
- Previous step results in <fastagent:steps>
|
|
180
|
+
- Task results and their attribution in <fastagent:task-result>
|
|
181
|
+
|
|
182
|
+
Provide a direct, focused response that addresses the task.
|
|
183
|
+
</fastagent:instruction>
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
SYNTHESIZE_STEP_PROMPT_TEMPLATE = """You need to synthesize the results of parallel tasks into a cohesive result.
|
|
187
|
+
|
|
188
|
+
<fastagent:data>
|
|
189
|
+
<fastagent:step-results>
|
|
190
|
+
{step_result}
|
|
191
|
+
</fastagent:step-results>
|
|
192
|
+
</fastagent:data>
|
|
193
|
+
|
|
194
|
+
<fastagent:instruction>
|
|
195
|
+
Analyze the results from all tasks in this step.
|
|
196
|
+
Each task was executed by a specific agent (finder, writer, etc.)
|
|
197
|
+
Consider the expertise of each agent when weighing their results.
|
|
198
|
+
Combine the information into a coherent, unified response.
|
|
199
|
+
Focus on key insights and important outcomes.
|
|
200
|
+
Resolve any conflicting information if present.
|
|
201
|
+
</fastagent:instruction>
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
SYNTHESIZE_PLAN_PROMPT_TEMPLATE = """You need to synthesize the results of all completed plan steps into a final response.
|
|
205
|
+
|
|
206
|
+
<fastagent:data>
|
|
207
|
+
<fastagent:plan-results>
|
|
208
|
+
{plan_result}
|
|
209
|
+
</fastagent:plan-results>
|
|
210
|
+
</fastagent:data>
|
|
211
|
+
|
|
212
|
+
<fastagent:instruction>
|
|
213
|
+
Create a comprehensive final response that addresses the original objective.
|
|
214
|
+
Integrate all the information gathered across all plan steps.
|
|
215
|
+
Provide a clear, complete answer that achieves the objective.
|
|
216
|
+
Focus on delivering value through your synthesis, not just summarizing.
|
|
217
|
+
|
|
218
|
+
If the plan was marked as incomplete but the maximum number of iterations was reached,
|
|
219
|
+
make sure to state clearly what was accomplished and what remains to be done.
|
|
220
|
+
</fastagent:instruction>
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
# New template for incomplete plans due to iteration limits
|
|
224
|
+
SYNTHESIZE_INCOMPLETE_PLAN_TEMPLATE = """You need to synthesize the results of all completed plan steps into a final response.
|
|
225
|
+
|
|
226
|
+
<fastagent:data>
|
|
227
|
+
<fastagent:plan-results>
|
|
228
|
+
{plan_result}
|
|
229
|
+
</fastagent:plan-results>
|
|
230
|
+
</fastagent:data>
|
|
231
|
+
|
|
232
|
+
<fastagent:status>
|
|
233
|
+
The maximum number of iterations ({max_iterations}) was reached before the objective could be completed.
|
|
234
|
+
</fastagent:status>
|
|
235
|
+
|
|
236
|
+
<fastagent:instruction>
|
|
237
|
+
Create a comprehensive response that summarizes what was accomplished so far.
|
|
238
|
+
The objective was NOT fully completed due to reaching the maximum number of iterations.
|
|
239
|
+
|
|
240
|
+
In your response:
|
|
241
|
+
1. Clearly state that the objective was not fully completed
|
|
242
|
+
2. Summarize what WAS accomplished across all the executed steps
|
|
243
|
+
3. Identify what remains to be done to complete the objective
|
|
244
|
+
4. Organize the information to provide maximum value despite being incomplete
|
|
245
|
+
|
|
246
|
+
Focus on being transparent about the incomplete status while providing as much value as possible.
|
|
247
|
+
</fastagent:instruction>
|
|
248
|
+
"""
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Any, List, Optional, Tuple
|
|
3
|
+
|
|
4
|
+
from mcp import Tool
|
|
5
|
+
from mcp.types import TextContent
|
|
6
|
+
from opentelemetry import trace
|
|
7
|
+
|
|
8
|
+
from fast_agent.agents.agent_types import AgentConfig, AgentType
|
|
9
|
+
from fast_agent.agents.llm_agent import LlmAgent
|
|
10
|
+
from fast_agent.core.logging.logger import get_logger
|
|
11
|
+
from fast_agent.interfaces import AgentProtocol, ModelT
|
|
12
|
+
from fast_agent.types import PromptMessageExtended, RequestParams
|
|
13
|
+
|
|
14
|
+
logger = get_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ParallelAgent(LlmAgent):
|
|
18
|
+
"""
|
|
19
|
+
LLMs can sometimes work simultaneously on a task (fan-out)
|
|
20
|
+
and have their outputs aggregated programmatically (fan-in).
|
|
21
|
+
This workflow performs both the fan-out and fan-in operations using LLMs.
|
|
22
|
+
From the user's perspective, an input is specified and the output is returned.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def agent_type(self) -> AgentType:
|
|
27
|
+
"""Return the type of this agent."""
|
|
28
|
+
return AgentType.PARALLEL
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
config: AgentConfig,
|
|
33
|
+
fan_in_agent: AgentProtocol,
|
|
34
|
+
fan_out_agents: List[AgentProtocol],
|
|
35
|
+
include_request: bool = True,
|
|
36
|
+
**kwargs,
|
|
37
|
+
) -> None:
|
|
38
|
+
"""
|
|
39
|
+
Initialize a ParallelLLM agent.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
config: Agent configuration or name
|
|
43
|
+
fan_in_agent: Agent that aggregates results from fan-out agents
|
|
44
|
+
fan_out_agents: List of agents to execute in parallel
|
|
45
|
+
include_request: Whether to include the original request in the aggregation
|
|
46
|
+
**kwargs: Additional keyword arguments to pass to BaseAgent
|
|
47
|
+
"""
|
|
48
|
+
super().__init__(config, **kwargs)
|
|
49
|
+
self.fan_in_agent = fan_in_agent
|
|
50
|
+
self.fan_out_agents = fan_out_agents
|
|
51
|
+
self.include_request = include_request
|
|
52
|
+
|
|
53
|
+
async def generate_impl(
|
|
54
|
+
self,
|
|
55
|
+
messages: List[PromptMessageExtended],
|
|
56
|
+
request_params: Optional[RequestParams] = None,
|
|
57
|
+
tools: List[Tool] | None = None,
|
|
58
|
+
) -> PromptMessageExtended:
|
|
59
|
+
"""
|
|
60
|
+
Execute fan-out agents in parallel and aggregate their results with the fan-in agent.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
normalized_messages: Already normalized list of PromptMessageExtended
|
|
64
|
+
request_params: Optional parameters to configure the request
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
The aggregated response from the fan-in agent
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
tracer = trace.get_tracer(__name__)
|
|
71
|
+
with tracer.start_as_current_span(f"Parallel: '{self._name}' generate"):
|
|
72
|
+
responses: List[PromptMessageExtended] = await self._execute_fan_out(
|
|
73
|
+
messages, request_params
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Extract the received message from the input
|
|
77
|
+
received_message: Optional[str] = messages[-1].all_text() if messages else None
|
|
78
|
+
|
|
79
|
+
# Convert responses to strings for aggregation
|
|
80
|
+
string_responses = []
|
|
81
|
+
for response in responses:
|
|
82
|
+
string_responses.append(response.all_text())
|
|
83
|
+
|
|
84
|
+
# Format the responses and send to the fan-in agent
|
|
85
|
+
aggregated_prompt = self._format_responses(string_responses, received_message)
|
|
86
|
+
|
|
87
|
+
# Create a new multipart message with the formatted responses
|
|
88
|
+
formatted_prompt = PromptMessageExtended(
|
|
89
|
+
role="user", content=[TextContent(type="text", text=aggregated_prompt)]
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Use the fan-in agent to aggregate the responses
|
|
93
|
+
return await self._fan_in_generate(formatted_prompt, request_params)
|
|
94
|
+
|
|
95
|
+
def _format_responses(self, responses: List[Any], message: Optional[str] = None) -> str:
|
|
96
|
+
"""
|
|
97
|
+
Format a list of responses for the fan-in agent.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
responses: List of responses from fan-out agents
|
|
101
|
+
message: Optional original message that was sent to the agents
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Formatted string with responses
|
|
105
|
+
"""
|
|
106
|
+
formatted = []
|
|
107
|
+
|
|
108
|
+
# Include the original message if specified
|
|
109
|
+
if self.include_request and message:
|
|
110
|
+
formatted.append("The following request was sent to the agents:")
|
|
111
|
+
formatted.append(f"<fastagent:request>\n{message}\n</fastagent:request>")
|
|
112
|
+
|
|
113
|
+
# Format each agent's response
|
|
114
|
+
for i, response in enumerate(responses):
|
|
115
|
+
agent_name = self.fan_out_agents[i].name
|
|
116
|
+
formatted.append(
|
|
117
|
+
f'<fastagent:response agent="{agent_name}">\n{response}\n</fastagent:response>'
|
|
118
|
+
)
|
|
119
|
+
return "\n\n".join(formatted)
|
|
120
|
+
|
|
121
|
+
async def structured_impl(
|
|
122
|
+
self,
|
|
123
|
+
messages: List[PromptMessageExtended],
|
|
124
|
+
model: type[ModelT],
|
|
125
|
+
request_params: Optional[RequestParams] = None,
|
|
126
|
+
) -> Tuple[ModelT | None, PromptMessageExtended]:
|
|
127
|
+
"""
|
|
128
|
+
Apply the prompt and return the result as a Pydantic model.
|
|
129
|
+
|
|
130
|
+
This implementation delegates to the fan-in agent's structured method.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
messages: List of PromptMessageExtended objects
|
|
134
|
+
model: The Pydantic model class to parse the result into
|
|
135
|
+
request_params: Optional parameters to configure the LLM request
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
An instance of the specified model, or None if coercion fails
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
tracer = trace.get_tracer(__name__)
|
|
142
|
+
with tracer.start_as_current_span(f"Parallel: '{self._name}' generate"):
|
|
143
|
+
responses: List[PromptMessageExtended] = await self._execute_fan_out(
|
|
144
|
+
messages, request_params
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# Extract the received message
|
|
148
|
+
received_message: Optional[str] = messages[-1].all_text() if messages else None
|
|
149
|
+
|
|
150
|
+
# Convert responses to strings
|
|
151
|
+
string_responses = [response.all_text() for response in responses]
|
|
152
|
+
|
|
153
|
+
# Format the responses for the fan-in agent
|
|
154
|
+
aggregated_prompt = self._format_responses(string_responses, received_message)
|
|
155
|
+
|
|
156
|
+
# Create a multipart message
|
|
157
|
+
formatted_prompt = PromptMessageExtended(
|
|
158
|
+
role="user", content=[TextContent(type="text", text=aggregated_prompt)]
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# Use the fan-in agent to parse the structured output
|
|
162
|
+
return await self._fan_in_structured(formatted_prompt, model, request_params)
|
|
163
|
+
|
|
164
|
+
async def initialize(self) -> None:
|
|
165
|
+
"""
|
|
166
|
+
Initialize the agent and its fan-in and fan-out agents.
|
|
167
|
+
"""
|
|
168
|
+
await super().initialize()
|
|
169
|
+
|
|
170
|
+
# Initialize fan-in and fan-out agents if not already initialized
|
|
171
|
+
if not self.fan_in_agent.initialized:
|
|
172
|
+
await self.fan_in_agent.initialize()
|
|
173
|
+
|
|
174
|
+
for agent in self.fan_out_agents:
|
|
175
|
+
if not agent.initialized:
|
|
176
|
+
await agent.initialize()
|
|
177
|
+
|
|
178
|
+
async def shutdown(self) -> None:
|
|
179
|
+
"""
|
|
180
|
+
Shutdown the agent and its fan-in and fan-out agents.
|
|
181
|
+
"""
|
|
182
|
+
await super().shutdown()
|
|
183
|
+
|
|
184
|
+
# Shutdown fan-in and fan-out agents
|
|
185
|
+
try:
|
|
186
|
+
await self.fan_in_agent.shutdown()
|
|
187
|
+
except Exception as e:
|
|
188
|
+
logger.warning(f"Error shutting down fan-in agent: {str(e)}")
|
|
189
|
+
|
|
190
|
+
for agent in self.fan_out_agents:
|
|
191
|
+
try:
|
|
192
|
+
await agent.shutdown()
|
|
193
|
+
except Exception as e:
|
|
194
|
+
logger.warning(f"Error shutting down fan-out agent {agent.name}: {str(e)}")
|
|
195
|
+
|
|
196
|
+
async def _execute_fan_out(
|
|
197
|
+
self,
|
|
198
|
+
messages: List[PromptMessageExtended],
|
|
199
|
+
request_params: Optional[RequestParams],
|
|
200
|
+
) -> List[PromptMessageExtended]:
|
|
201
|
+
"""
|
|
202
|
+
Run fan-out agents with telemetry so transports can surface progress.
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
async def _run_agent(agent: AgentProtocol) -> PromptMessageExtended:
|
|
206
|
+
async with self.workflow_telemetry.start_step(
|
|
207
|
+
"parallel.fan_out",
|
|
208
|
+
server_name=self.name,
|
|
209
|
+
arguments={"agent": agent.name},
|
|
210
|
+
) as step:
|
|
211
|
+
result = await agent.generate(messages, request_params)
|
|
212
|
+
await step.finish(True, text=f"{agent.name} completed fan-out work")
|
|
213
|
+
return result
|
|
214
|
+
|
|
215
|
+
return await asyncio.gather(*[_run_agent(agent) for agent in self.fan_out_agents])
|
|
216
|
+
|
|
217
|
+
async def _fan_in_generate(
|
|
218
|
+
self,
|
|
219
|
+
prompt: PromptMessageExtended,
|
|
220
|
+
request_params: Optional[RequestParams],
|
|
221
|
+
) -> PromptMessageExtended:
|
|
222
|
+
"""
|
|
223
|
+
Aggregate fan-out output with telemetry.
|
|
224
|
+
"""
|
|
225
|
+
async with self.workflow_telemetry.start_step(
|
|
226
|
+
"parallel.fan_in",
|
|
227
|
+
server_name=self.name,
|
|
228
|
+
arguments={"agent": self.fan_in_agent.name},
|
|
229
|
+
) as step:
|
|
230
|
+
result = await self.fan_in_agent.generate([prompt], request_params)
|
|
231
|
+
await step.finish(True, text=f"{self.fan_in_agent.name} aggregated results")
|
|
232
|
+
return result
|
|
233
|
+
|
|
234
|
+
async def _fan_in_structured(
|
|
235
|
+
self,
|
|
236
|
+
prompt: PromptMessageExtended,
|
|
237
|
+
model: type[ModelT],
|
|
238
|
+
request_params: Optional[RequestParams],
|
|
239
|
+
) -> Tuple[ModelT | None, PromptMessageExtended]:
|
|
240
|
+
"""
|
|
241
|
+
Structured aggregation with telemetry.
|
|
242
|
+
"""
|
|
243
|
+
async with self.workflow_telemetry.start_step(
|
|
244
|
+
"parallel.fan_in_structured",
|
|
245
|
+
server_name=self.name,
|
|
246
|
+
arguments={"agent": self.fan_in_agent.name},
|
|
247
|
+
) as step:
|
|
248
|
+
result = await self.fan_in_agent.structured([prompt], model, request_params)
|
|
249
|
+
await step.finish(True, text=f"{self.fan_in_agent.name} produced structured output")
|
|
250
|
+
return result
|