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,557 @@
|
|
|
1
|
+
"""Bootstrap command to create example applications."""
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
from contextlib import ExitStack
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from importlib.resources import as_file, files
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.panel import Panel
|
|
12
|
+
from rich.table import Table
|
|
13
|
+
|
|
14
|
+
from fast_agent.ui.console import console as shared_console
|
|
15
|
+
|
|
16
|
+
app = typer.Typer(
|
|
17
|
+
help="Create fast-agent quickstarts",
|
|
18
|
+
no_args_is_help=False, # Allow showing our custom help instead
|
|
19
|
+
)
|
|
20
|
+
console = shared_console
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
BASE_EXAMPLES_DIR = files("fast_agent").joinpath("resources").joinpath("examples")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class ExampleConfig:
|
|
28
|
+
description: str
|
|
29
|
+
files: list[str]
|
|
30
|
+
create_subdir: bool
|
|
31
|
+
path_in_examples: list[str]
|
|
32
|
+
mount_point_files: list[str] | None = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
_EXAMPLE_CONFIGS = {
|
|
36
|
+
"workflow": ExampleConfig(
|
|
37
|
+
description=(
|
|
38
|
+
"Example workflows, demonstrating each of the patterns in Anthropic's\n"
|
|
39
|
+
"'Building Effective Agents' paper. Some agents use the 'fetch'\n"
|
|
40
|
+
"and filesystem MCP Servers."
|
|
41
|
+
),
|
|
42
|
+
files=[
|
|
43
|
+
"chaining.py",
|
|
44
|
+
"evaluator.py",
|
|
45
|
+
"human_input.py",
|
|
46
|
+
"orchestrator.py",
|
|
47
|
+
"parallel.py",
|
|
48
|
+
"router.py",
|
|
49
|
+
"short_story.txt",
|
|
50
|
+
"fastagent.config.yaml",
|
|
51
|
+
],
|
|
52
|
+
create_subdir=True,
|
|
53
|
+
path_in_examples=["workflows"],
|
|
54
|
+
),
|
|
55
|
+
"researcher": ExampleConfig(
|
|
56
|
+
description=(
|
|
57
|
+
"Research agent example with additional evaluation/optimization\n"
|
|
58
|
+
"example. Uses Brave Search and Docker MCP Servers.\n"
|
|
59
|
+
"Creates examples in a 'researcher' subdirectory."
|
|
60
|
+
),
|
|
61
|
+
files=["researcher.py", "researcher-eval.py", "fastagent.config.yaml"],
|
|
62
|
+
create_subdir=True,
|
|
63
|
+
path_in_examples=["researcher"],
|
|
64
|
+
),
|
|
65
|
+
"data-analysis": ExampleConfig(
|
|
66
|
+
description=(
|
|
67
|
+
"Data analysis agent examples that demonstrate working with\n"
|
|
68
|
+
"datasets, performing statistical analysis, and generating visualizations.\n"
|
|
69
|
+
"Creates examples in a 'data-analysis' subdirectory with mount-point for data.\n"
|
|
70
|
+
"Uses MCP 'roots' feature for mapping"
|
|
71
|
+
),
|
|
72
|
+
files=["analysis.py", "fastagent.config.yaml"],
|
|
73
|
+
mount_point_files=["WA_Fn-UseC_-HR-Employee-Attrition.csv"],
|
|
74
|
+
create_subdir=True,
|
|
75
|
+
path_in_examples=["data-analysis"],
|
|
76
|
+
),
|
|
77
|
+
"state-transfer": ExampleConfig(
|
|
78
|
+
description=(
|
|
79
|
+
"Example demonstrating state transfer between multiple agents.\n"
|
|
80
|
+
"Shows how state can be passed between agent runs to maintain context.\n"
|
|
81
|
+
"Creates examples in a 'state-transfer' subdirectory."
|
|
82
|
+
),
|
|
83
|
+
files=[
|
|
84
|
+
"agent_one.py",
|
|
85
|
+
"agent_two.py",
|
|
86
|
+
"fastagent.config.yaml",
|
|
87
|
+
"fastagent.secrets.yaml.example",
|
|
88
|
+
],
|
|
89
|
+
create_subdir=True,
|
|
90
|
+
path_in_examples=["mcp", "state-transfer"],
|
|
91
|
+
),
|
|
92
|
+
"elicitations": ExampleConfig(
|
|
93
|
+
description=(
|
|
94
|
+
"Interactive form examples using MCP elicitations feature.\n"
|
|
95
|
+
"Demonstrates collecting structured data with forms, AI-guided workflows,\n"
|
|
96
|
+
"and custom handlers. Creates examples in an 'elicitations' subdirectory."
|
|
97
|
+
),
|
|
98
|
+
files=[
|
|
99
|
+
"elicitation_account_server.py",
|
|
100
|
+
"elicitation_forms_server.py",
|
|
101
|
+
"elicitation_game_server.py",
|
|
102
|
+
"fastagent.config.yaml",
|
|
103
|
+
"fastagent.secrets.yaml.example",
|
|
104
|
+
"forms_demo.py",
|
|
105
|
+
"game_character.py",
|
|
106
|
+
"game_character_handler.py",
|
|
107
|
+
"tool_call.py",
|
|
108
|
+
],
|
|
109
|
+
create_subdir=True,
|
|
110
|
+
path_in_examples=["mcp", "elicitations"],
|
|
111
|
+
),
|
|
112
|
+
"tensorzero": ExampleConfig(
|
|
113
|
+
description=(
|
|
114
|
+
"A complete example showcasing the TensorZero integration.\n"
|
|
115
|
+
"Includes the T0 Gateway, an MCP server, an interactive agent, and \n"
|
|
116
|
+
"multi-modal functionality."
|
|
117
|
+
),
|
|
118
|
+
files=[
|
|
119
|
+
".env.sample",
|
|
120
|
+
"Makefile",
|
|
121
|
+
"README.md",
|
|
122
|
+
"agent.py",
|
|
123
|
+
"docker-compose.yml",
|
|
124
|
+
"fastagent.config.yaml",
|
|
125
|
+
"image_demo.py",
|
|
126
|
+
"simple_agent.py",
|
|
127
|
+
"mcp_server/",
|
|
128
|
+
"demo_images/",
|
|
129
|
+
"tensorzero_config/",
|
|
130
|
+
],
|
|
131
|
+
create_subdir=True,
|
|
132
|
+
path_in_examples=["elicitations"],
|
|
133
|
+
),
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _development_mode_fallback(example_info: ExampleConfig) -> Path:
|
|
138
|
+
"""Fallback function for development mode."""
|
|
139
|
+
package_dir = Path(__file__).parent.parent.parent.parent.parent
|
|
140
|
+
for dir in example_info.path_in_examples:
|
|
141
|
+
package_dir = package_dir / dir
|
|
142
|
+
console.print(f"[blue]Using development directory: {package_dir}[/blue]")
|
|
143
|
+
return package_dir
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def copy_example_files(example_type: str, target_dir: Path, force: bool = False) -> list[str]:
|
|
147
|
+
"""Copy example files from resources to target directory."""
|
|
148
|
+
# Determine if we should create a subdirectory for this example type
|
|
149
|
+
example_info = _EXAMPLE_CONFIGS.get(example_type, None)
|
|
150
|
+
if example_info is None:
|
|
151
|
+
console.print(f"Example type '{example_type}' not found.")
|
|
152
|
+
return []
|
|
153
|
+
|
|
154
|
+
if example_info.create_subdir:
|
|
155
|
+
target_dir = target_dir / example_type
|
|
156
|
+
if not target_dir.exists():
|
|
157
|
+
target_dir.mkdir(parents=True)
|
|
158
|
+
console.print(f"Created subdirectory: {target_dir}")
|
|
159
|
+
|
|
160
|
+
# Determine source directory - try package resources first, then fallback
|
|
161
|
+
use_as_file = False
|
|
162
|
+
# Try to use examples from the installed package first, or fall back to the top-level directory
|
|
163
|
+
try:
|
|
164
|
+
# First try to find examples in the package resources
|
|
165
|
+
source_dir_traversable = BASE_EXAMPLES_DIR
|
|
166
|
+
for dir in example_info.path_in_examples:
|
|
167
|
+
source_dir_traversable = source_dir_traversable.joinpath(dir)
|
|
168
|
+
|
|
169
|
+
# Check if we found a valid directory
|
|
170
|
+
if not source_dir_traversable.is_dir():
|
|
171
|
+
console.print(
|
|
172
|
+
f"[yellow]Resource directory not found: {source_dir_traversable}. "
|
|
173
|
+
"Falling back to development mode.[/yellow]"
|
|
174
|
+
)
|
|
175
|
+
# Fall back to the top-level directory for development mode
|
|
176
|
+
source_dir: Path = _development_mode_fallback(example_info)
|
|
177
|
+
else:
|
|
178
|
+
# We have a valid Traversable, will need to use as_file
|
|
179
|
+
source_dir = source_dir_traversable # type: ignore
|
|
180
|
+
use_as_file = True
|
|
181
|
+
except (ImportError, ModuleNotFoundError, ValueError) as e:
|
|
182
|
+
console.print(
|
|
183
|
+
f"[yellow]Error accessing resources: {e}. Falling back to development mode.[/yellow]"
|
|
184
|
+
)
|
|
185
|
+
source_dir = _development_mode_fallback(example_info)
|
|
186
|
+
|
|
187
|
+
# Use as_file context manager if source_dir is a Traversable, otherwise use directly
|
|
188
|
+
with ExitStack() as stack:
|
|
189
|
+
if use_as_file:
|
|
190
|
+
source_path = stack.enter_context(as_file(source_dir)) # type: ignore
|
|
191
|
+
else:
|
|
192
|
+
source_path = source_dir
|
|
193
|
+
|
|
194
|
+
if not source_path.exists():
|
|
195
|
+
console.print(f"[red]Error: Source directory not found: {source_path}[/red]")
|
|
196
|
+
return []
|
|
197
|
+
|
|
198
|
+
return _copy_files_from_source(
|
|
199
|
+
example_type, example_info, source_path, target_dir, force
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _copy_files_from_source(
|
|
204
|
+
example_type: str, example_info: ExampleConfig, source_dir: Path, target_dir: Path, force: bool
|
|
205
|
+
) -> list[str]:
|
|
206
|
+
"""Helper function to copy files from a source directory."""
|
|
207
|
+
created = []
|
|
208
|
+
for filename in example_info.files:
|
|
209
|
+
source = source_dir / filename
|
|
210
|
+
target = target_dir / filename
|
|
211
|
+
|
|
212
|
+
try:
|
|
213
|
+
if not source.exists():
|
|
214
|
+
console.print(f"[red]Error: Source file not found: {source}[/red]")
|
|
215
|
+
continue
|
|
216
|
+
|
|
217
|
+
if target.exists() and not force:
|
|
218
|
+
console.print(f"[yellow]Skipping[/yellow] {filename} (already exists)")
|
|
219
|
+
continue
|
|
220
|
+
|
|
221
|
+
shutil.copy2(source, target)
|
|
222
|
+
try:
|
|
223
|
+
# This can fail in test environments where the target is not relative to target_dir.parent
|
|
224
|
+
rel_path = str(target.relative_to(target_dir.parent))
|
|
225
|
+
except ValueError:
|
|
226
|
+
# Fallback to just the filename
|
|
227
|
+
rel_path = f"{example_type}/{filename}"
|
|
228
|
+
|
|
229
|
+
created.append(rel_path)
|
|
230
|
+
console.print(f"[green]Created[/green] {rel_path}")
|
|
231
|
+
|
|
232
|
+
except Exception as e:
|
|
233
|
+
console.print(f"[red]Error copying {filename}: {str(e)}[/red]")
|
|
234
|
+
|
|
235
|
+
# Copy mount-point files if any
|
|
236
|
+
mount_point_files = example_info.mount_point_files or []
|
|
237
|
+
if mount_point_files:
|
|
238
|
+
mount_point_dir = target_dir / "mount-point"
|
|
239
|
+
|
|
240
|
+
# Create mount-point directory if needed
|
|
241
|
+
if not mount_point_dir.exists():
|
|
242
|
+
mount_point_dir.mkdir(parents=True)
|
|
243
|
+
console.print(f"Created mount-point directory: {mount_point_dir}")
|
|
244
|
+
|
|
245
|
+
for filename in mount_point_files:
|
|
246
|
+
source = source_dir / "mount-point" / filename
|
|
247
|
+
target = mount_point_dir / filename
|
|
248
|
+
|
|
249
|
+
try:
|
|
250
|
+
if not source.exists():
|
|
251
|
+
console.print(f"[red]Error: Source file not found: {source}[/red]")
|
|
252
|
+
continue
|
|
253
|
+
|
|
254
|
+
if target.exists() and not force:
|
|
255
|
+
console.print(
|
|
256
|
+
f"[yellow]Skipping[/yellow] mount-point/{filename} (already exists)"
|
|
257
|
+
)
|
|
258
|
+
continue
|
|
259
|
+
|
|
260
|
+
shutil.copy2(source, target)
|
|
261
|
+
created.append(f"{example_type}/mount-point/{filename}")
|
|
262
|
+
console.print(f"[green]Created[/green] mount-point/{filename}")
|
|
263
|
+
|
|
264
|
+
except Exception as e:
|
|
265
|
+
console.print(f"[red]Error copying mount-point/{filename}: {str(e)}[/red]")
|
|
266
|
+
|
|
267
|
+
return created
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def copy_project_template(source_dir: Path, dest_dir: Path, console: Console, force: bool = False):
|
|
271
|
+
"""
|
|
272
|
+
Recursively copies a project template directory.
|
|
273
|
+
This is a helper to handle project-based quickstarts like TensorZero.
|
|
274
|
+
"""
|
|
275
|
+
if dest_dir.exists():
|
|
276
|
+
if force:
|
|
277
|
+
console.print(
|
|
278
|
+
f"[yellow]--force specified. Removing existing directory: {dest_dir}[/yellow]"
|
|
279
|
+
)
|
|
280
|
+
shutil.rmtree(dest_dir)
|
|
281
|
+
else:
|
|
282
|
+
console.print(
|
|
283
|
+
f"[bold yellow]Directory '{dest_dir.name}' already exists.[/bold yellow] Use --force to overwrite."
|
|
284
|
+
)
|
|
285
|
+
return False
|
|
286
|
+
|
|
287
|
+
try:
|
|
288
|
+
shutil.copytree(source_dir, dest_dir)
|
|
289
|
+
return True
|
|
290
|
+
except Exception as e:
|
|
291
|
+
console.print(f"[red]Error copying project template: {e}[/red]")
|
|
292
|
+
return False
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def show_overview() -> None:
|
|
296
|
+
"""Display an overview of available examples in a nicely formatted table."""
|
|
297
|
+
console.print("\n[bold cyan]fast-agent quickstarts[/bold cyan]")
|
|
298
|
+
console.print("Build agents and compose workflows through practical examples\n")
|
|
299
|
+
|
|
300
|
+
# Create a table for better organization
|
|
301
|
+
table = Table(show_header=True, header_style="bold magenta", box=None, padding=(0, 2))
|
|
302
|
+
table.add_column("Example")
|
|
303
|
+
table.add_column("Description")
|
|
304
|
+
table.add_column("Files")
|
|
305
|
+
|
|
306
|
+
for name, info in _EXAMPLE_CONFIGS.items():
|
|
307
|
+
# Just show file count instead of listing all files
|
|
308
|
+
file_count = len(info.files)
|
|
309
|
+
files_summary = f"{file_count} files"
|
|
310
|
+
mount_files = info.mount_point_files
|
|
311
|
+
if mount_files:
|
|
312
|
+
files_summary += f"\n+ {len(mount_files)} data files"
|
|
313
|
+
table.add_row(f"[green]{name}[/green]", info.description, files_summary)
|
|
314
|
+
|
|
315
|
+
console.print(table)
|
|
316
|
+
|
|
317
|
+
# Show usage instructions in a panel
|
|
318
|
+
usage_text = (
|
|
319
|
+
"[bold]Usage:[/bold]\n"
|
|
320
|
+
" [cyan]fast-agent[/cyan] [green]quickstart[/green] [yellow]<name>[/yellow] [dim]\\[directory][/dim]\n\n"
|
|
321
|
+
"[dim]directory optionally overrides the default subdirectory name[/dim]\n\n"
|
|
322
|
+
"[bold]Options:[/bold]\n"
|
|
323
|
+
" [cyan]--force[/cyan] Overwrite existing files"
|
|
324
|
+
)
|
|
325
|
+
console.print(Panel(usage_text, title="Usage", border_style="blue"))
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
@app.command()
|
|
329
|
+
def workflow(
|
|
330
|
+
directory: Path = typer.Argument(
|
|
331
|
+
Path("."),
|
|
332
|
+
help="Directory where workflow examples will be created",
|
|
333
|
+
),
|
|
334
|
+
force: bool = typer.Option(False, "--force", "-f", help="Force overwrite existing files"),
|
|
335
|
+
) -> None:
|
|
336
|
+
"""Create workflow pattern examples."""
|
|
337
|
+
target_dir = directory.resolve()
|
|
338
|
+
if not target_dir.exists():
|
|
339
|
+
target_dir.mkdir(parents=True)
|
|
340
|
+
console.print(f"Created directory: {target_dir}")
|
|
341
|
+
|
|
342
|
+
created = copy_example_files("workflow", target_dir, force)
|
|
343
|
+
_show_completion_message("workflow", created)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
@app.command()
|
|
347
|
+
def researcher(
|
|
348
|
+
directory: Path = typer.Argument(
|
|
349
|
+
Path("."),
|
|
350
|
+
help="Directory where researcher examples will be created (in 'researcher' subdirectory)",
|
|
351
|
+
),
|
|
352
|
+
force: bool = typer.Option(False, "--force", "-f", help="Force overwrite existing files"),
|
|
353
|
+
) -> None:
|
|
354
|
+
"""Create researcher pattern examples."""
|
|
355
|
+
target_dir = directory.resolve()
|
|
356
|
+
if not target_dir.exists():
|
|
357
|
+
target_dir.mkdir(parents=True)
|
|
358
|
+
console.print(f"Created directory: {target_dir}")
|
|
359
|
+
|
|
360
|
+
created = copy_example_files("researcher", target_dir, force)
|
|
361
|
+
_show_completion_message("researcher", created)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
@app.command()
|
|
365
|
+
def data_analysis(
|
|
366
|
+
directory: Path = typer.Argument(
|
|
367
|
+
Path("."),
|
|
368
|
+
help="Directory where data analysis examples will be created (creates 'data-analysis' subdirectory with mount-point)",
|
|
369
|
+
),
|
|
370
|
+
force: bool = typer.Option(False, "--force", "-f", help="Force overwrite existing files"),
|
|
371
|
+
) -> None:
|
|
372
|
+
"""Create data analysis examples with sample dataset."""
|
|
373
|
+
target_dir = directory.resolve()
|
|
374
|
+
if not target_dir.exists():
|
|
375
|
+
target_dir.mkdir(parents=True)
|
|
376
|
+
console.print(f"Created directory: {target_dir}")
|
|
377
|
+
|
|
378
|
+
created = copy_example_files("data-analysis", target_dir, force)
|
|
379
|
+
_show_completion_message("data-analysis", created)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
@app.command()
|
|
383
|
+
def state_transfer(
|
|
384
|
+
directory: Path = typer.Argument(
|
|
385
|
+
Path("."),
|
|
386
|
+
help="Directory where state transfer examples will be created (in 'state-transfer' subdirectory)",
|
|
387
|
+
),
|
|
388
|
+
force: bool = typer.Option(False, "--force", "-f", help="Force overwrite existing files"),
|
|
389
|
+
) -> None:
|
|
390
|
+
"""Create state transfer example showing state passing between agents."""
|
|
391
|
+
target_dir = directory.resolve()
|
|
392
|
+
if not target_dir.exists():
|
|
393
|
+
target_dir.mkdir(parents=True)
|
|
394
|
+
console.print(f"Created directory: {target_dir}")
|
|
395
|
+
|
|
396
|
+
created = copy_example_files("state-transfer", target_dir, force)
|
|
397
|
+
_show_completion_message("state-transfer", created)
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
@app.command()
|
|
401
|
+
def elicitations(
|
|
402
|
+
directory: Path = typer.Argument(
|
|
403
|
+
Path("."),
|
|
404
|
+
help="Directory where elicitation examples will be created (in 'elicitations' subdirectory)",
|
|
405
|
+
),
|
|
406
|
+
force: bool = typer.Option(False, "--force", "-f", help="Force overwrite existing files"),
|
|
407
|
+
) -> None:
|
|
408
|
+
"""Create interactive form examples using MCP elicitations."""
|
|
409
|
+
target_dir = directory.resolve()
|
|
410
|
+
if not target_dir.exists():
|
|
411
|
+
target_dir.mkdir(parents=True)
|
|
412
|
+
console.print(f"Created directory: {target_dir}")
|
|
413
|
+
|
|
414
|
+
created = copy_example_files("elicitations", target_dir, force)
|
|
415
|
+
_show_completion_message("elicitations", created)
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def _show_completion_message(example_type: str, created: list[str]) -> None:
|
|
419
|
+
"""Show completion message and next steps."""
|
|
420
|
+
if created:
|
|
421
|
+
console.print("\n[green]Setup completed successfully![/green]")
|
|
422
|
+
console.print("\nCreated files:")
|
|
423
|
+
for f in created:
|
|
424
|
+
console.print(f" - {f}")
|
|
425
|
+
|
|
426
|
+
console.print("\n[bold]Next Steps:[/bold]")
|
|
427
|
+
if example_type == "workflow":
|
|
428
|
+
console.print("1. Review chaining.py for the basic workflow example")
|
|
429
|
+
console.print("2. Check other examples:")
|
|
430
|
+
console.print(" - parallel.py: Run agents in parallel")
|
|
431
|
+
console.print(" - router.py: Route requests between agents")
|
|
432
|
+
console.print(" - evaluator.py: Add evaluation capabilities")
|
|
433
|
+
console.print(" - human_input.py: Incorporate human feedback")
|
|
434
|
+
console.print("3. Run an example with: uv run <example>.py")
|
|
435
|
+
console.print(
|
|
436
|
+
"4. Try a different model with --model=<model>, or update the agent config"
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
elif example_type == "researcher":
|
|
440
|
+
console.print(
|
|
441
|
+
"1. Set up the Brave MCP Server (get an API key from https://brave.com/search/api/)"
|
|
442
|
+
)
|
|
443
|
+
console.print("2. Try `uv run researcher.py` for the basic version")
|
|
444
|
+
console.print("3. Try `uv run researcher-eval.py` for the eval/optimize version")
|
|
445
|
+
elif example_type == "data-analysis":
|
|
446
|
+
console.print("1. Run uv `analysis.py` to perform data analysis and visualization")
|
|
447
|
+
console.print("2. The dataset is available in the mount-point directory:")
|
|
448
|
+
console.print(" - mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv")
|
|
449
|
+
console.print(
|
|
450
|
+
"On Windows platforms, please edit the fastagent.config.yaml and adjust the volume mount point."
|
|
451
|
+
)
|
|
452
|
+
elif example_type == "state-transfer":
|
|
453
|
+
console.print(
|
|
454
|
+
"Check [cyan][link=https://fast-agent.ai]fast-agent.ai[/link][/cyan] for quick start walkthroughs"
|
|
455
|
+
)
|
|
456
|
+
elif example_type == "elicitations":
|
|
457
|
+
console.print("1. Go to the `elicitations` subdirectory (cd elicitations)")
|
|
458
|
+
console.print("2. Try the forms demo: uv run forms_demo.py")
|
|
459
|
+
console.print("3. Run the game character creator: uv run game_character.py")
|
|
460
|
+
console.print(
|
|
461
|
+
"Check [cyan][link=https://fast-agent.ai/mcp/elicitations/]https://fast-agent.ai/mcp/elicitations/[/link][/cyan] for more details"
|
|
462
|
+
)
|
|
463
|
+
else:
|
|
464
|
+
console.print("\n[yellow]No files were created.[/yellow]")
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
@app.command(name="tensorzero", help="Create the TensorZero integration example project.")
|
|
468
|
+
def tensorzero(
|
|
469
|
+
directory: Path = typer.Argument(
|
|
470
|
+
Path("."),
|
|
471
|
+
help="Directory where the 'tensorzero' project folder will be created.",
|
|
472
|
+
),
|
|
473
|
+
force: bool = typer.Option(
|
|
474
|
+
False, "--force", "-f", help="Force overwrite if project directory exists"
|
|
475
|
+
),
|
|
476
|
+
):
|
|
477
|
+
"""Create the TensorZero project example."""
|
|
478
|
+
console.print("[bold green]Setting up the TensorZero quickstart example...[/bold green]")
|
|
479
|
+
|
|
480
|
+
dest_project_dir = directory.resolve() / "tensorzero"
|
|
481
|
+
|
|
482
|
+
# --- Find Source Directory ---
|
|
483
|
+
use_as_file = False
|
|
484
|
+
try:
|
|
485
|
+
# This path MUST match the "to" path from hatch_build.py
|
|
486
|
+
source_dir_traversable = (
|
|
487
|
+
files("fast_agent").joinpath("resources").joinpath("examples").joinpath("tensorzero")
|
|
488
|
+
)
|
|
489
|
+
if not source_dir_traversable.is_dir():
|
|
490
|
+
raise FileNotFoundError # Fallback to dev mode if resource isn't a dir
|
|
491
|
+
source_dir = source_dir_traversable # type: ignore
|
|
492
|
+
use_as_file = True
|
|
493
|
+
except (ImportError, ModuleNotFoundError, FileNotFoundError):
|
|
494
|
+
console.print(
|
|
495
|
+
"[yellow]Package resources not found. Falling back to development mode.[/yellow]"
|
|
496
|
+
)
|
|
497
|
+
# This path is relative to the project root in a development environment
|
|
498
|
+
source_dir = Path(__file__).parent.parent.parent.parent / "examples" / "tensorzero"
|
|
499
|
+
|
|
500
|
+
# Use as_file context manager if needed
|
|
501
|
+
with ExitStack() as stack:
|
|
502
|
+
if use_as_file:
|
|
503
|
+
source_path = stack.enter_context(as_file(source_dir)) # type: ignore
|
|
504
|
+
else:
|
|
505
|
+
source_path = source_dir # type: ignore[assignment]
|
|
506
|
+
|
|
507
|
+
if not source_path.exists() or not source_path.is_dir():
|
|
508
|
+
console.print(f"[red]Error: Source project directory not found at '{source_path}'[/red]")
|
|
509
|
+
raise typer.Exit(1)
|
|
510
|
+
|
|
511
|
+
console.print(f"Source directory: [dim]{source_path}[/dim]")
|
|
512
|
+
console.print(f"Destination: [dim]{dest_project_dir}[/dim]")
|
|
513
|
+
|
|
514
|
+
# --- Copy Project and Show Message ---
|
|
515
|
+
if copy_project_template(source_path, dest_project_dir, console, force):
|
|
516
|
+
console.print(
|
|
517
|
+
f"\n[bold green]✅ Success![/bold green] Your TensorZero project has been created in: [cyan]{dest_project_dir}[/cyan]"
|
|
518
|
+
)
|
|
519
|
+
console.print("\n[bold yellow]Next Steps:[/bold yellow]")
|
|
520
|
+
console.print("\n1. [bold]Navigate to your new project directory:[/bold]")
|
|
521
|
+
console.print(f" [cyan]cd {dest_project_dir.relative_to(Path.cwd())}[/cyan]")
|
|
522
|
+
|
|
523
|
+
console.print("\n2. [bold]Set up your API keys:[/bold]")
|
|
524
|
+
console.print(" [cyan]cp .env.sample .env[/cyan]")
|
|
525
|
+
console.print(
|
|
526
|
+
" [dim]Then, open the new '.env' file and add your OpenAI or Anthropic API key.[/dim]"
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
console.print(
|
|
530
|
+
"\n3. [bold]Start the required services (TensorZero Gateway & MCP Server):[/bold]"
|
|
531
|
+
)
|
|
532
|
+
console.print(" [cyan]docker compose up --build -d[/cyan]")
|
|
533
|
+
console.print(
|
|
534
|
+
" [dim](This builds and starts the necessary containers in the background)[/dim]"
|
|
535
|
+
)
|
|
536
|
+
|
|
537
|
+
console.print("\n4. [bold]Run the interactive agent:[/bold]")
|
|
538
|
+
console.print(" [cyan]make agent[/cyan] (or `uv run agent.py`)")
|
|
539
|
+
console.print("\nEnjoy exploring the TensorZero integration with fast-agent! ✨")
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
@app.command(name="t0", help="Alias for the TensorZero quickstart.", hidden=True)
|
|
543
|
+
def t0_alias(
|
|
544
|
+
directory: Path = typer.Argument(
|
|
545
|
+
Path("."), help="Directory for the 'tensorzero' project folder."
|
|
546
|
+
),
|
|
547
|
+
force: bool = typer.Option(False, "--force", "-f", help="Force overwrite"),
|
|
548
|
+
):
|
|
549
|
+
"""Alias for the `tensorzero` command."""
|
|
550
|
+
tensorzero(directory, force)
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
@app.callback(invoke_without_command=True)
|
|
554
|
+
def main(ctx: typer.Context) -> None:
|
|
555
|
+
"""Quickstart applications for fast-agent."""
|
|
556
|
+
if ctx.invoked_subcommand is None:
|
|
557
|
+
show_overview()
|