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,514 @@
|
|
|
1
|
+
"""Run an interactive agent directly from the command line."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
import shlex
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Literal
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
|
|
12
|
+
from fast_agent import FastAgent
|
|
13
|
+
from fast_agent.agents.llm_agent import LlmAgent
|
|
14
|
+
from fast_agent.cli.commands.server_helpers import add_servers_to_config, generate_server_name
|
|
15
|
+
from fast_agent.cli.commands.url_parser import generate_server_configs, parse_server_urls
|
|
16
|
+
from fast_agent.constants import DEFAULT_AGENT_INSTRUCTION
|
|
17
|
+
from fast_agent.ui.console_display import ConsoleDisplay
|
|
18
|
+
|
|
19
|
+
app = typer.Typer(
|
|
20
|
+
help="Run an interactive agent directly from the command line without creating an agent.py file",
|
|
21
|
+
context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
default_instruction = DEFAULT_AGENT_INSTRUCTION
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def resolve_instruction_option(instruction: str | None) -> tuple[str, str]:
|
|
28
|
+
"""
|
|
29
|
+
Resolve the instruction option (file or URL) to the instruction string and agent name.
|
|
30
|
+
Returns (resolved_instruction, agent_name).
|
|
31
|
+
"""
|
|
32
|
+
resolved_instruction = default_instruction
|
|
33
|
+
agent_name = "agent"
|
|
34
|
+
|
|
35
|
+
if instruction:
|
|
36
|
+
try:
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
|
|
39
|
+
from pydantic import AnyUrl
|
|
40
|
+
|
|
41
|
+
from fast_agent.core.direct_decorators import _resolve_instruction
|
|
42
|
+
|
|
43
|
+
if instruction.startswith(("http://", "https://")):
|
|
44
|
+
resolved_instruction = _resolve_instruction(AnyUrl(instruction))
|
|
45
|
+
else:
|
|
46
|
+
resolved_instruction = _resolve_instruction(Path(instruction))
|
|
47
|
+
instruction_path = Path(instruction)
|
|
48
|
+
if instruction_path.exists() and instruction_path.is_file():
|
|
49
|
+
agent_name = instruction_path.stem
|
|
50
|
+
except Exception as e:
|
|
51
|
+
typer.echo(f"Error loading instruction from {instruction}: {e}", err=True)
|
|
52
|
+
raise typer.Exit(1)
|
|
53
|
+
|
|
54
|
+
return resolved_instruction, agent_name
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def collect_stdio_commands(npx: str | None, uvx: str | None, stdio: str | None) -> list[str]:
|
|
58
|
+
"""Collect STDIO command definitions from convenience options."""
|
|
59
|
+
stdio_commands: list[str] = []
|
|
60
|
+
|
|
61
|
+
if npx:
|
|
62
|
+
stdio_commands.append(f"npx {npx}")
|
|
63
|
+
if uvx:
|
|
64
|
+
stdio_commands.append(f"uvx {uvx}")
|
|
65
|
+
if stdio:
|
|
66
|
+
stdio_commands.append(stdio)
|
|
67
|
+
|
|
68
|
+
return stdio_commands
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _set_asyncio_exception_handler(loop: asyncio.AbstractEventLoop) -> None:
|
|
72
|
+
"""Attach a detailed exception handler to the provided event loop."""
|
|
73
|
+
|
|
74
|
+
logger = logging.getLogger("fast_agent.asyncio")
|
|
75
|
+
|
|
76
|
+
def _handler(_loop: asyncio.AbstractEventLoop, context: dict) -> None:
|
|
77
|
+
message = context.get("message", "(no message)")
|
|
78
|
+
task = context.get("task")
|
|
79
|
+
future = context.get("future")
|
|
80
|
+
handle = context.get("handle")
|
|
81
|
+
source_traceback = context.get("source_traceback")
|
|
82
|
+
exception = context.get("exception")
|
|
83
|
+
|
|
84
|
+
details = {
|
|
85
|
+
"message": message,
|
|
86
|
+
"task": repr(task) if task else None,
|
|
87
|
+
"future": repr(future) if future else None,
|
|
88
|
+
"handle": repr(handle) if handle else None,
|
|
89
|
+
"source_traceback": [str(frame) for frame in source_traceback]
|
|
90
|
+
if source_traceback
|
|
91
|
+
else None,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
logger.error("Unhandled asyncio error: %s", message)
|
|
95
|
+
logger.error("Asyncio context: %s", details)
|
|
96
|
+
|
|
97
|
+
if exception:
|
|
98
|
+
logger.exception("Asyncio exception", exc_info=exception)
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
loop.set_exception_handler(_handler)
|
|
102
|
+
except Exception:
|
|
103
|
+
logger = logging.getLogger("fast_agent.asyncio")
|
|
104
|
+
logger.exception("Failed to set asyncio exception handler")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
async def _run_agent(
|
|
108
|
+
name: str = "fast-agent cli",
|
|
109
|
+
instruction: str = default_instruction,
|
|
110
|
+
config_path: str | None = None,
|
|
111
|
+
server_list: list[str] | None = None,
|
|
112
|
+
model: str | None = None,
|
|
113
|
+
message: str | None = None,
|
|
114
|
+
prompt_file: str | None = None,
|
|
115
|
+
url_servers: dict[str, dict[str, str]] | None = None,
|
|
116
|
+
stdio_servers: dict[str, dict[str, str]] | None = None,
|
|
117
|
+
agent_name: str | None = "agent",
|
|
118
|
+
skills_directory: Path | None = None,
|
|
119
|
+
shell_runtime: bool = False,
|
|
120
|
+
mode: Literal["interactive", "serve"] = "interactive",
|
|
121
|
+
transport: str = "http",
|
|
122
|
+
host: str = "0.0.0.0",
|
|
123
|
+
port: int = 8000,
|
|
124
|
+
tool_description: str | None = None,
|
|
125
|
+
instance_scope: str = "shared",
|
|
126
|
+
permissions_enabled: bool = True,
|
|
127
|
+
) -> None:
|
|
128
|
+
"""Async implementation to run an interactive agent."""
|
|
129
|
+
from fast_agent.mcp.prompts.prompt_load import load_prompt
|
|
130
|
+
|
|
131
|
+
# Create the FastAgent instance
|
|
132
|
+
|
|
133
|
+
fast_kwargs = {
|
|
134
|
+
"name": name,
|
|
135
|
+
"config_path": config_path,
|
|
136
|
+
"ignore_unknown_args": True,
|
|
137
|
+
"parse_cli_args": False, # Don't parse CLI args, we're handling it ourselves
|
|
138
|
+
}
|
|
139
|
+
if mode == "serve":
|
|
140
|
+
fast_kwargs["quiet"] = True
|
|
141
|
+
if skills_directory is not None:
|
|
142
|
+
fast_kwargs["skills_directory"] = skills_directory
|
|
143
|
+
|
|
144
|
+
fast = FastAgent(**fast_kwargs)
|
|
145
|
+
|
|
146
|
+
# Set model on args so model source detection works correctly
|
|
147
|
+
if model:
|
|
148
|
+
fast.args.model = model
|
|
149
|
+
|
|
150
|
+
if shell_runtime:
|
|
151
|
+
await fast.app.initialize()
|
|
152
|
+
setattr(fast.app.context, "shell_runtime", True)
|
|
153
|
+
|
|
154
|
+
# Add all dynamic servers to the configuration
|
|
155
|
+
await add_servers_to_config(fast, url_servers)
|
|
156
|
+
await add_servers_to_config(fast, stdio_servers)
|
|
157
|
+
|
|
158
|
+
# Check if we have multiple models (comma-delimited)
|
|
159
|
+
if model and "," in model:
|
|
160
|
+
# Parse multiple models
|
|
161
|
+
models = [m.strip() for m in model.split(",") if m.strip()]
|
|
162
|
+
|
|
163
|
+
# Create an agent for each model
|
|
164
|
+
fan_out_agents = []
|
|
165
|
+
for i, model_name in enumerate(models):
|
|
166
|
+
agent_name = f"{model_name}"
|
|
167
|
+
|
|
168
|
+
# Define the agent with specified parameters
|
|
169
|
+
agent_kwargs = {"instruction": instruction, "name": agent_name}
|
|
170
|
+
if server_list:
|
|
171
|
+
agent_kwargs["servers"] = server_list
|
|
172
|
+
agent_kwargs["model"] = model_name
|
|
173
|
+
|
|
174
|
+
@fast.agent(**agent_kwargs)
|
|
175
|
+
async def model_agent():
|
|
176
|
+
pass
|
|
177
|
+
|
|
178
|
+
fan_out_agents.append(agent_name)
|
|
179
|
+
|
|
180
|
+
# Create a silent fan-in agent (suppresses display output)
|
|
181
|
+
class SilentFanInAgent(LlmAgent):
|
|
182
|
+
async def show_assistant_message(self, *args, **kwargs): # type: ignore[override]
|
|
183
|
+
return None
|
|
184
|
+
|
|
185
|
+
def show_user_message(self, *args, **kwargs): # type: ignore[override]
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
@fast.custom(
|
|
189
|
+
SilentFanInAgent,
|
|
190
|
+
name="aggregate",
|
|
191
|
+
model="passthrough",
|
|
192
|
+
instruction="You aggregate parallel outputs without displaying intermediate messages.",
|
|
193
|
+
)
|
|
194
|
+
async def aggregate():
|
|
195
|
+
pass
|
|
196
|
+
|
|
197
|
+
# Create a parallel agent with silent fan_in
|
|
198
|
+
@fast.parallel(
|
|
199
|
+
name="parallel",
|
|
200
|
+
fan_out=fan_out_agents,
|
|
201
|
+
fan_in="aggregate",
|
|
202
|
+
include_request=True,
|
|
203
|
+
default=True,
|
|
204
|
+
)
|
|
205
|
+
async def cli_agent():
|
|
206
|
+
async with fast.run() as agent:
|
|
207
|
+
if message:
|
|
208
|
+
await agent.parallel.send(message)
|
|
209
|
+
display = ConsoleDisplay(config=None)
|
|
210
|
+
display.show_parallel_results(agent.parallel)
|
|
211
|
+
elif prompt_file:
|
|
212
|
+
prompt = load_prompt(Path(prompt_file))
|
|
213
|
+
await agent.parallel.generate(prompt)
|
|
214
|
+
display = ConsoleDisplay(config=None)
|
|
215
|
+
display.show_parallel_results(agent.parallel)
|
|
216
|
+
else:
|
|
217
|
+
await agent.interactive(pretty_print_parallel=True)
|
|
218
|
+
else:
|
|
219
|
+
# Single model - use original behavior
|
|
220
|
+
# Define the agent with specified parameters
|
|
221
|
+
agent_kwargs = {"instruction": instruction}
|
|
222
|
+
if agent_name:
|
|
223
|
+
agent_kwargs["name"] = agent_name
|
|
224
|
+
if server_list:
|
|
225
|
+
agent_kwargs["servers"] = server_list
|
|
226
|
+
if model:
|
|
227
|
+
agent_kwargs["model"] = model
|
|
228
|
+
|
|
229
|
+
@fast.agent(**agent_kwargs)
|
|
230
|
+
async def cli_agent():
|
|
231
|
+
async with fast.run() as agent:
|
|
232
|
+
if message:
|
|
233
|
+
response = await agent.send(message)
|
|
234
|
+
# Print the response and exit
|
|
235
|
+
print(response)
|
|
236
|
+
elif prompt_file:
|
|
237
|
+
prompt = load_prompt(Path(prompt_file))
|
|
238
|
+
response = await agent.agent.generate(prompt)
|
|
239
|
+
print(f"\nLoaded {len(prompt)} messages from prompt file '{prompt_file}'")
|
|
240
|
+
await agent.interactive()
|
|
241
|
+
else:
|
|
242
|
+
await agent.interactive()
|
|
243
|
+
|
|
244
|
+
# Run the agent
|
|
245
|
+
if mode == "serve":
|
|
246
|
+
await fast.start_server(
|
|
247
|
+
transport=transport,
|
|
248
|
+
host=host,
|
|
249
|
+
port=port,
|
|
250
|
+
tool_description=tool_description,
|
|
251
|
+
instance_scope=instance_scope,
|
|
252
|
+
permissions_enabled=permissions_enabled,
|
|
253
|
+
)
|
|
254
|
+
else:
|
|
255
|
+
await cli_agent()
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def run_async_agent(
|
|
259
|
+
name: str,
|
|
260
|
+
instruction: str,
|
|
261
|
+
config_path: str | None = None,
|
|
262
|
+
servers: str | None = None,
|
|
263
|
+
urls: str | None = None,
|
|
264
|
+
auth: str | None = None,
|
|
265
|
+
model: str | None = None,
|
|
266
|
+
message: str | None = None,
|
|
267
|
+
prompt_file: str | None = None,
|
|
268
|
+
stdio_commands: list[str] | None = None,
|
|
269
|
+
agent_name: str | None = None,
|
|
270
|
+
skills_directory: Path | None = None,
|
|
271
|
+
shell_enabled: bool = False,
|
|
272
|
+
mode: Literal["interactive", "serve"] = "interactive",
|
|
273
|
+
transport: str = "http",
|
|
274
|
+
host: str = "0.0.0.0",
|
|
275
|
+
port: int = 8000,
|
|
276
|
+
tool_description: str | None = None,
|
|
277
|
+
instance_scope: str = "shared",
|
|
278
|
+
permissions_enabled: bool = True,
|
|
279
|
+
):
|
|
280
|
+
"""Run the async agent function with proper loop handling."""
|
|
281
|
+
server_list = servers.split(",") if servers else None
|
|
282
|
+
|
|
283
|
+
# Parse URLs and generate server configurations if provided
|
|
284
|
+
url_servers = None
|
|
285
|
+
if urls:
|
|
286
|
+
try:
|
|
287
|
+
parsed_urls = parse_server_urls(urls, auth)
|
|
288
|
+
url_servers = generate_server_configs(parsed_urls)
|
|
289
|
+
# If we have servers from URLs, add their names to the server_list
|
|
290
|
+
if url_servers and not server_list:
|
|
291
|
+
server_list = list(url_servers.keys())
|
|
292
|
+
elif url_servers and server_list:
|
|
293
|
+
# Merge both lists
|
|
294
|
+
server_list.extend(list(url_servers.keys()))
|
|
295
|
+
except ValueError as e:
|
|
296
|
+
print(f"Error parsing URLs: {e}", file=sys.stderr)
|
|
297
|
+
sys.exit(1)
|
|
298
|
+
|
|
299
|
+
# Generate STDIO server configurations if provided
|
|
300
|
+
stdio_servers = None
|
|
301
|
+
|
|
302
|
+
if stdio_commands:
|
|
303
|
+
stdio_servers = {}
|
|
304
|
+
for i, stdio_cmd in enumerate(stdio_commands):
|
|
305
|
+
# Parse the stdio command string
|
|
306
|
+
try:
|
|
307
|
+
parsed_command = shlex.split(stdio_cmd)
|
|
308
|
+
if not parsed_command:
|
|
309
|
+
print(f"Error: Empty stdio command: {stdio_cmd}", file=sys.stderr)
|
|
310
|
+
continue
|
|
311
|
+
|
|
312
|
+
command = parsed_command[0]
|
|
313
|
+
initial_args = parsed_command[1:] if len(parsed_command) > 1 else []
|
|
314
|
+
|
|
315
|
+
# Generate a server name from the command
|
|
316
|
+
if initial_args:
|
|
317
|
+
# Try to extract a meaningful name from the args
|
|
318
|
+
for arg in initial_args:
|
|
319
|
+
if arg.endswith(".py") or arg.endswith(".js") or arg.endswith(".ts"):
|
|
320
|
+
base_name = generate_server_name(arg)
|
|
321
|
+
break
|
|
322
|
+
else:
|
|
323
|
+
# Fallback to command name
|
|
324
|
+
base_name = generate_server_name(command)
|
|
325
|
+
else:
|
|
326
|
+
base_name = generate_server_name(command)
|
|
327
|
+
|
|
328
|
+
# Ensure unique server names when multiple servers
|
|
329
|
+
server_name = base_name
|
|
330
|
+
if len(stdio_commands) > 1:
|
|
331
|
+
server_name = f"{base_name}_{i + 1}"
|
|
332
|
+
|
|
333
|
+
# Build the complete args list
|
|
334
|
+
stdio_command_args = initial_args.copy()
|
|
335
|
+
|
|
336
|
+
# Add this server to the configuration
|
|
337
|
+
stdio_servers[server_name] = {
|
|
338
|
+
"transport": "stdio",
|
|
339
|
+
"command": command,
|
|
340
|
+
"args": stdio_command_args,
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
# Add STDIO server to the server list
|
|
344
|
+
if not server_list:
|
|
345
|
+
server_list = [server_name]
|
|
346
|
+
else:
|
|
347
|
+
server_list.append(server_name)
|
|
348
|
+
|
|
349
|
+
except ValueError as e:
|
|
350
|
+
print(f"Error parsing stdio command '{stdio_cmd}': {e}", file=sys.stderr)
|
|
351
|
+
continue
|
|
352
|
+
|
|
353
|
+
# Check if we're already in an event loop
|
|
354
|
+
try:
|
|
355
|
+
loop = asyncio.get_event_loop()
|
|
356
|
+
if loop.is_running():
|
|
357
|
+
# We're inside a running event loop, so we can't use asyncio.run
|
|
358
|
+
# Instead, create a new loop
|
|
359
|
+
loop = asyncio.new_event_loop()
|
|
360
|
+
asyncio.set_event_loop(loop)
|
|
361
|
+
_set_asyncio_exception_handler(loop)
|
|
362
|
+
except RuntimeError:
|
|
363
|
+
# No event loop exists, so we'll create one
|
|
364
|
+
loop = asyncio.new_event_loop()
|
|
365
|
+
asyncio.set_event_loop(loop)
|
|
366
|
+
_set_asyncio_exception_handler(loop)
|
|
367
|
+
|
|
368
|
+
try:
|
|
369
|
+
loop.run_until_complete(
|
|
370
|
+
_run_agent(
|
|
371
|
+
name=name,
|
|
372
|
+
instruction=instruction,
|
|
373
|
+
config_path=config_path,
|
|
374
|
+
server_list=server_list,
|
|
375
|
+
model=model,
|
|
376
|
+
message=message,
|
|
377
|
+
prompt_file=prompt_file,
|
|
378
|
+
url_servers=url_servers,
|
|
379
|
+
stdio_servers=stdio_servers,
|
|
380
|
+
agent_name=agent_name,
|
|
381
|
+
skills_directory=skills_directory,
|
|
382
|
+
shell_runtime=shell_enabled,
|
|
383
|
+
mode=mode,
|
|
384
|
+
transport=transport,
|
|
385
|
+
host=host,
|
|
386
|
+
port=port,
|
|
387
|
+
tool_description=tool_description,
|
|
388
|
+
instance_scope=instance_scope,
|
|
389
|
+
permissions_enabled=permissions_enabled,
|
|
390
|
+
)
|
|
391
|
+
)
|
|
392
|
+
finally:
|
|
393
|
+
try:
|
|
394
|
+
# Clean up the loop
|
|
395
|
+
tasks = asyncio.all_tasks(loop)
|
|
396
|
+
for task in tasks:
|
|
397
|
+
task.cancel()
|
|
398
|
+
|
|
399
|
+
# Run the event loop until all tasks are done
|
|
400
|
+
if sys.version_info >= (3, 7):
|
|
401
|
+
loop.run_until_complete(asyncio.gather(*tasks, return_exceptions=True))
|
|
402
|
+
loop.run_until_complete(loop.shutdown_asyncgens())
|
|
403
|
+
loop.close()
|
|
404
|
+
except Exception:
|
|
405
|
+
pass
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
@app.callback(invoke_without_command=True, no_args_is_help=False)
|
|
409
|
+
def go(
|
|
410
|
+
ctx: typer.Context,
|
|
411
|
+
name: str = typer.Option("fast-agent", "--name", help="Name for the agent"),
|
|
412
|
+
instruction: str | None = typer.Option(
|
|
413
|
+
None, "--instruction", "-i", help="Path to file or URL containing instruction for the agent"
|
|
414
|
+
),
|
|
415
|
+
config_path: str | None = typer.Option(None, "--config-path", "-c", help="Path to config file"),
|
|
416
|
+
servers: str | None = typer.Option(
|
|
417
|
+
None, "--servers", help="Comma-separated list of server names to enable from config"
|
|
418
|
+
),
|
|
419
|
+
urls: str | None = typer.Option(
|
|
420
|
+
None, "--url", help="Comma-separated list of HTTP/SSE URLs to connect to"
|
|
421
|
+
),
|
|
422
|
+
auth: str | None = typer.Option(
|
|
423
|
+
None, "--auth", help="Bearer token for authorization with URL-based servers"
|
|
424
|
+
),
|
|
425
|
+
model: str | None = typer.Option(
|
|
426
|
+
None, "--model", "--models", help="Override the default model (e.g., haiku, sonnet, gpt-4)"
|
|
427
|
+
),
|
|
428
|
+
message: str | None = typer.Option(
|
|
429
|
+
None, "--message", "-m", help="Message to send to the agent (skips interactive mode)"
|
|
430
|
+
),
|
|
431
|
+
prompt_file: str | None = typer.Option(
|
|
432
|
+
None, "--prompt-file", "-p", help="Path to a prompt file to use (either text or JSON)"
|
|
433
|
+
),
|
|
434
|
+
skills_dir: Path | None = typer.Option(
|
|
435
|
+
None,
|
|
436
|
+
"--skills-dir",
|
|
437
|
+
"--skills",
|
|
438
|
+
help="Override the default skills directory",
|
|
439
|
+
),
|
|
440
|
+
npx: str | None = typer.Option(
|
|
441
|
+
None, "--npx", help="NPX package and args to run as MCP server (quoted)"
|
|
442
|
+
),
|
|
443
|
+
uvx: str | None = typer.Option(
|
|
444
|
+
None, "--uvx", help="UVX package and args to run as MCP server (quoted)"
|
|
445
|
+
),
|
|
446
|
+
stdio: str | None = typer.Option(
|
|
447
|
+
None, "--stdio", help="Command to run as STDIO MCP server (quoted)"
|
|
448
|
+
),
|
|
449
|
+
shell: bool = typer.Option(
|
|
450
|
+
False,
|
|
451
|
+
"--shell",
|
|
452
|
+
"-x",
|
|
453
|
+
help="Enable a local shell runtime and expose the execute tool (bash or pwsh).",
|
|
454
|
+
),
|
|
455
|
+
) -> None:
|
|
456
|
+
"""
|
|
457
|
+
Run an interactive agent directly from the command line.
|
|
458
|
+
|
|
459
|
+
Examples:
|
|
460
|
+
fast-agent go --model=haiku --instruction=./instruction.md --servers=fetch,filesystem
|
|
461
|
+
fast-agent go --instruction=https://raw.githubusercontent.com/user/repo/prompt.md
|
|
462
|
+
fast-agent go --message="What is the weather today?" --model=haiku
|
|
463
|
+
fast-agent go --prompt-file=my-prompt.txt --model=haiku
|
|
464
|
+
fast-agent go --url=http://localhost:8001/mcp,http://api.example.com/sse
|
|
465
|
+
fast-agent go --url=https://api.example.com/mcp --auth=YOUR_API_TOKEN
|
|
466
|
+
fast-agent go --npx "@modelcontextprotocol/server-filesystem /path/to/data"
|
|
467
|
+
fast-agent go --uvx "mcp-server-fetch --verbose"
|
|
468
|
+
fast-agent go --stdio "python my_server.py --debug"
|
|
469
|
+
fast-agent go --stdio "uv run server.py --config=settings.json"
|
|
470
|
+
fast-agent go --skills /path/to/myskills -x
|
|
471
|
+
|
|
472
|
+
This will start an interactive session with the agent, using the specified model
|
|
473
|
+
and instruction. It will use the default configuration from fastagent.config.yaml
|
|
474
|
+
unless --config-path is specified.
|
|
475
|
+
|
|
476
|
+
Common options:
|
|
477
|
+
--model Override the default model (e.g., --model=haiku)
|
|
478
|
+
--quiet Disable progress display and logging
|
|
479
|
+
--servers Comma-separated list of server names to enable from config
|
|
480
|
+
--url Comma-separated list of HTTP/SSE URLs to connect to
|
|
481
|
+
--auth Bearer token for authorization with URL-based servers
|
|
482
|
+
--message, -m Send a single message and exit
|
|
483
|
+
--prompt-file, -p Use a prompt file instead of interactive mode
|
|
484
|
+
--skills Override the default skills folder
|
|
485
|
+
--shell, -x Enable local shell runtime
|
|
486
|
+
--npx NPX package and args to run as MCP server (quoted)
|
|
487
|
+
--uvx UVX package and args to run as MCP server (quoted)
|
|
488
|
+
--stdio Command to run as STDIO MCP server (quoted)
|
|
489
|
+
"""
|
|
490
|
+
# Collect all stdio commands from convenience options
|
|
491
|
+
stdio_commands = collect_stdio_commands(npx, uvx, stdio)
|
|
492
|
+
shell_enabled = shell
|
|
493
|
+
|
|
494
|
+
# When shell is enabled we don't add an MCP stdio server; handled inside the agent
|
|
495
|
+
|
|
496
|
+
# Resolve instruction from file/URL or use default
|
|
497
|
+
resolved_instruction, agent_name = resolve_instruction_option(instruction)
|
|
498
|
+
|
|
499
|
+
run_async_agent(
|
|
500
|
+
name=name,
|
|
501
|
+
instruction=resolved_instruction,
|
|
502
|
+
config_path=config_path,
|
|
503
|
+
servers=servers,
|
|
504
|
+
urls=urls,
|
|
505
|
+
auth=auth,
|
|
506
|
+
model=model,
|
|
507
|
+
message=message,
|
|
508
|
+
prompt_file=prompt_file,
|
|
509
|
+
stdio_commands=stdio_commands,
|
|
510
|
+
agent_name=agent_name,
|
|
511
|
+
skills_directory=skills_dir,
|
|
512
|
+
shell_enabled=shell_enabled,
|
|
513
|
+
instance_scope="shared",
|
|
514
|
+
)
|