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.
Files changed (261) hide show
  1. fast_agent/__init__.py +183 -0
  2. fast_agent/acp/__init__.py +19 -0
  3. fast_agent/acp/acp_aware_mixin.py +304 -0
  4. fast_agent/acp/acp_context.py +437 -0
  5. fast_agent/acp/content_conversion.py +136 -0
  6. fast_agent/acp/filesystem_runtime.py +427 -0
  7. fast_agent/acp/permission_store.py +269 -0
  8. fast_agent/acp/server/__init__.py +5 -0
  9. fast_agent/acp/server/agent_acp_server.py +1472 -0
  10. fast_agent/acp/slash_commands.py +1050 -0
  11. fast_agent/acp/terminal_runtime.py +408 -0
  12. fast_agent/acp/tool_permission_adapter.py +125 -0
  13. fast_agent/acp/tool_permissions.py +474 -0
  14. fast_agent/acp/tool_progress.py +814 -0
  15. fast_agent/agents/__init__.py +85 -0
  16. fast_agent/agents/agent_types.py +64 -0
  17. fast_agent/agents/llm_agent.py +350 -0
  18. fast_agent/agents/llm_decorator.py +1139 -0
  19. fast_agent/agents/mcp_agent.py +1337 -0
  20. fast_agent/agents/tool_agent.py +271 -0
  21. fast_agent/agents/workflow/agents_as_tools_agent.py +849 -0
  22. fast_agent/agents/workflow/chain_agent.py +212 -0
  23. fast_agent/agents/workflow/evaluator_optimizer.py +380 -0
  24. fast_agent/agents/workflow/iterative_planner.py +652 -0
  25. fast_agent/agents/workflow/maker_agent.py +379 -0
  26. fast_agent/agents/workflow/orchestrator_models.py +218 -0
  27. fast_agent/agents/workflow/orchestrator_prompts.py +248 -0
  28. fast_agent/agents/workflow/parallel_agent.py +250 -0
  29. fast_agent/agents/workflow/router_agent.py +353 -0
  30. fast_agent/cli/__init__.py +0 -0
  31. fast_agent/cli/__main__.py +73 -0
  32. fast_agent/cli/commands/acp.py +159 -0
  33. fast_agent/cli/commands/auth.py +404 -0
  34. fast_agent/cli/commands/check_config.py +783 -0
  35. fast_agent/cli/commands/go.py +514 -0
  36. fast_agent/cli/commands/quickstart.py +557 -0
  37. fast_agent/cli/commands/serve.py +143 -0
  38. fast_agent/cli/commands/server_helpers.py +114 -0
  39. fast_agent/cli/commands/setup.py +174 -0
  40. fast_agent/cli/commands/url_parser.py +190 -0
  41. fast_agent/cli/constants.py +40 -0
  42. fast_agent/cli/main.py +115 -0
  43. fast_agent/cli/terminal.py +24 -0
  44. fast_agent/config.py +798 -0
  45. fast_agent/constants.py +41 -0
  46. fast_agent/context.py +279 -0
  47. fast_agent/context_dependent.py +50 -0
  48. fast_agent/core/__init__.py +92 -0
  49. fast_agent/core/agent_app.py +448 -0
  50. fast_agent/core/core_app.py +137 -0
  51. fast_agent/core/direct_decorators.py +784 -0
  52. fast_agent/core/direct_factory.py +620 -0
  53. fast_agent/core/error_handling.py +27 -0
  54. fast_agent/core/exceptions.py +90 -0
  55. fast_agent/core/executor/__init__.py +0 -0
  56. fast_agent/core/executor/executor.py +280 -0
  57. fast_agent/core/executor/task_registry.py +32 -0
  58. fast_agent/core/executor/workflow_signal.py +324 -0
  59. fast_agent/core/fastagent.py +1186 -0
  60. fast_agent/core/logging/__init__.py +5 -0
  61. fast_agent/core/logging/events.py +138 -0
  62. fast_agent/core/logging/json_serializer.py +164 -0
  63. fast_agent/core/logging/listeners.py +309 -0
  64. fast_agent/core/logging/logger.py +278 -0
  65. fast_agent/core/logging/transport.py +481 -0
  66. fast_agent/core/prompt.py +9 -0
  67. fast_agent/core/prompt_templates.py +183 -0
  68. fast_agent/core/validation.py +326 -0
  69. fast_agent/event_progress.py +62 -0
  70. fast_agent/history/history_exporter.py +49 -0
  71. fast_agent/human_input/__init__.py +47 -0
  72. fast_agent/human_input/elicitation_handler.py +123 -0
  73. fast_agent/human_input/elicitation_state.py +33 -0
  74. fast_agent/human_input/form_elements.py +59 -0
  75. fast_agent/human_input/form_fields.py +256 -0
  76. fast_agent/human_input/simple_form.py +113 -0
  77. fast_agent/human_input/types.py +40 -0
  78. fast_agent/interfaces.py +310 -0
  79. fast_agent/llm/__init__.py +9 -0
  80. fast_agent/llm/cancellation.py +22 -0
  81. fast_agent/llm/fastagent_llm.py +931 -0
  82. fast_agent/llm/internal/passthrough.py +161 -0
  83. fast_agent/llm/internal/playback.py +129 -0
  84. fast_agent/llm/internal/silent.py +41 -0
  85. fast_agent/llm/internal/slow.py +38 -0
  86. fast_agent/llm/memory.py +275 -0
  87. fast_agent/llm/model_database.py +490 -0
  88. fast_agent/llm/model_factory.py +388 -0
  89. fast_agent/llm/model_info.py +102 -0
  90. fast_agent/llm/prompt_utils.py +155 -0
  91. fast_agent/llm/provider/anthropic/anthropic_utils.py +84 -0
  92. fast_agent/llm/provider/anthropic/cache_planner.py +56 -0
  93. fast_agent/llm/provider/anthropic/llm_anthropic.py +796 -0
  94. fast_agent/llm/provider/anthropic/multipart_converter_anthropic.py +462 -0
  95. fast_agent/llm/provider/bedrock/bedrock_utils.py +218 -0
  96. fast_agent/llm/provider/bedrock/llm_bedrock.py +2207 -0
  97. fast_agent/llm/provider/bedrock/multipart_converter_bedrock.py +84 -0
  98. fast_agent/llm/provider/google/google_converter.py +466 -0
  99. fast_agent/llm/provider/google/llm_google_native.py +681 -0
  100. fast_agent/llm/provider/openai/llm_aliyun.py +31 -0
  101. fast_agent/llm/provider/openai/llm_azure.py +143 -0
  102. fast_agent/llm/provider/openai/llm_deepseek.py +76 -0
  103. fast_agent/llm/provider/openai/llm_generic.py +35 -0
  104. fast_agent/llm/provider/openai/llm_google_oai.py +32 -0
  105. fast_agent/llm/provider/openai/llm_groq.py +42 -0
  106. fast_agent/llm/provider/openai/llm_huggingface.py +85 -0
  107. fast_agent/llm/provider/openai/llm_openai.py +1195 -0
  108. fast_agent/llm/provider/openai/llm_openai_compatible.py +138 -0
  109. fast_agent/llm/provider/openai/llm_openrouter.py +45 -0
  110. fast_agent/llm/provider/openai/llm_tensorzero_openai.py +128 -0
  111. fast_agent/llm/provider/openai/llm_xai.py +38 -0
  112. fast_agent/llm/provider/openai/multipart_converter_openai.py +561 -0
  113. fast_agent/llm/provider/openai/openai_multipart.py +169 -0
  114. fast_agent/llm/provider/openai/openai_utils.py +67 -0
  115. fast_agent/llm/provider/openai/responses.py +133 -0
  116. fast_agent/llm/provider_key_manager.py +139 -0
  117. fast_agent/llm/provider_types.py +34 -0
  118. fast_agent/llm/request_params.py +61 -0
  119. fast_agent/llm/sampling_converter.py +98 -0
  120. fast_agent/llm/stream_types.py +9 -0
  121. fast_agent/llm/usage_tracking.py +445 -0
  122. fast_agent/mcp/__init__.py +56 -0
  123. fast_agent/mcp/common.py +26 -0
  124. fast_agent/mcp/elicitation_factory.py +84 -0
  125. fast_agent/mcp/elicitation_handlers.py +164 -0
  126. fast_agent/mcp/gen_client.py +83 -0
  127. fast_agent/mcp/helpers/__init__.py +36 -0
  128. fast_agent/mcp/helpers/content_helpers.py +352 -0
  129. fast_agent/mcp/helpers/server_config_helpers.py +25 -0
  130. fast_agent/mcp/hf_auth.py +147 -0
  131. fast_agent/mcp/interfaces.py +92 -0
  132. fast_agent/mcp/logger_textio.py +108 -0
  133. fast_agent/mcp/mcp_agent_client_session.py +411 -0
  134. fast_agent/mcp/mcp_aggregator.py +2175 -0
  135. fast_agent/mcp/mcp_connection_manager.py +723 -0
  136. fast_agent/mcp/mcp_content.py +262 -0
  137. fast_agent/mcp/mime_utils.py +108 -0
  138. fast_agent/mcp/oauth_client.py +509 -0
  139. fast_agent/mcp/prompt.py +159 -0
  140. fast_agent/mcp/prompt_message_extended.py +155 -0
  141. fast_agent/mcp/prompt_render.py +84 -0
  142. fast_agent/mcp/prompt_serialization.py +580 -0
  143. fast_agent/mcp/prompts/__init__.py +0 -0
  144. fast_agent/mcp/prompts/__main__.py +7 -0
  145. fast_agent/mcp/prompts/prompt_constants.py +18 -0
  146. fast_agent/mcp/prompts/prompt_helpers.py +238 -0
  147. fast_agent/mcp/prompts/prompt_load.py +186 -0
  148. fast_agent/mcp/prompts/prompt_server.py +552 -0
  149. fast_agent/mcp/prompts/prompt_template.py +438 -0
  150. fast_agent/mcp/resource_utils.py +215 -0
  151. fast_agent/mcp/sampling.py +200 -0
  152. fast_agent/mcp/server/__init__.py +4 -0
  153. fast_agent/mcp/server/agent_server.py +613 -0
  154. fast_agent/mcp/skybridge.py +44 -0
  155. fast_agent/mcp/sse_tracking.py +287 -0
  156. fast_agent/mcp/stdio_tracking_simple.py +59 -0
  157. fast_agent/mcp/streamable_http_tracking.py +309 -0
  158. fast_agent/mcp/tool_execution_handler.py +137 -0
  159. fast_agent/mcp/tool_permission_handler.py +88 -0
  160. fast_agent/mcp/transport_tracking.py +634 -0
  161. fast_agent/mcp/types.py +24 -0
  162. fast_agent/mcp/ui_agent.py +48 -0
  163. fast_agent/mcp/ui_mixin.py +209 -0
  164. fast_agent/mcp_server_registry.py +89 -0
  165. fast_agent/py.typed +0 -0
  166. fast_agent/resources/examples/data-analysis/analysis-campaign.py +189 -0
  167. fast_agent/resources/examples/data-analysis/analysis.py +68 -0
  168. fast_agent/resources/examples/data-analysis/fastagent.config.yaml +41 -0
  169. fast_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +1471 -0
  170. fast_agent/resources/examples/mcp/elicitations/elicitation_account_server.py +88 -0
  171. fast_agent/resources/examples/mcp/elicitations/elicitation_forms_server.py +297 -0
  172. fast_agent/resources/examples/mcp/elicitations/elicitation_game_server.py +164 -0
  173. fast_agent/resources/examples/mcp/elicitations/fastagent.config.yaml +35 -0
  174. fast_agent/resources/examples/mcp/elicitations/fastagent.secrets.yaml.example +17 -0
  175. fast_agent/resources/examples/mcp/elicitations/forms_demo.py +107 -0
  176. fast_agent/resources/examples/mcp/elicitations/game_character.py +65 -0
  177. fast_agent/resources/examples/mcp/elicitations/game_character_handler.py +256 -0
  178. fast_agent/resources/examples/mcp/elicitations/tool_call.py +21 -0
  179. fast_agent/resources/examples/mcp/state-transfer/agent_one.py +18 -0
  180. fast_agent/resources/examples/mcp/state-transfer/agent_two.py +18 -0
  181. fast_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml +27 -0
  182. fast_agent/resources/examples/mcp/state-transfer/fastagent.secrets.yaml.example +15 -0
  183. fast_agent/resources/examples/researcher/fastagent.config.yaml +61 -0
  184. fast_agent/resources/examples/researcher/researcher-eval.py +53 -0
  185. fast_agent/resources/examples/researcher/researcher-imp.py +189 -0
  186. fast_agent/resources/examples/researcher/researcher.py +36 -0
  187. fast_agent/resources/examples/tensorzero/.env.sample +2 -0
  188. fast_agent/resources/examples/tensorzero/Makefile +31 -0
  189. fast_agent/resources/examples/tensorzero/README.md +56 -0
  190. fast_agent/resources/examples/tensorzero/agent.py +35 -0
  191. fast_agent/resources/examples/tensorzero/demo_images/clam.jpg +0 -0
  192. fast_agent/resources/examples/tensorzero/demo_images/crab.png +0 -0
  193. fast_agent/resources/examples/tensorzero/demo_images/shrimp.png +0 -0
  194. fast_agent/resources/examples/tensorzero/docker-compose.yml +105 -0
  195. fast_agent/resources/examples/tensorzero/fastagent.config.yaml +19 -0
  196. fast_agent/resources/examples/tensorzero/image_demo.py +67 -0
  197. fast_agent/resources/examples/tensorzero/mcp_server/Dockerfile +25 -0
  198. fast_agent/resources/examples/tensorzero/mcp_server/entrypoint.sh +35 -0
  199. fast_agent/resources/examples/tensorzero/mcp_server/mcp_server.py +31 -0
  200. fast_agent/resources/examples/tensorzero/mcp_server/pyproject.toml +11 -0
  201. fast_agent/resources/examples/tensorzero/simple_agent.py +25 -0
  202. fast_agent/resources/examples/tensorzero/tensorzero_config/system_schema.json +29 -0
  203. fast_agent/resources/examples/tensorzero/tensorzero_config/system_template.minijinja +11 -0
  204. fast_agent/resources/examples/tensorzero/tensorzero_config/tensorzero.toml +35 -0
  205. fast_agent/resources/examples/workflows/agents_as_tools_extended.py +73 -0
  206. fast_agent/resources/examples/workflows/agents_as_tools_simple.py +50 -0
  207. fast_agent/resources/examples/workflows/chaining.py +37 -0
  208. fast_agent/resources/examples/workflows/evaluator.py +77 -0
  209. fast_agent/resources/examples/workflows/fastagent.config.yaml +26 -0
  210. fast_agent/resources/examples/workflows/graded_report.md +89 -0
  211. fast_agent/resources/examples/workflows/human_input.py +28 -0
  212. fast_agent/resources/examples/workflows/maker.py +156 -0
  213. fast_agent/resources/examples/workflows/orchestrator.py +70 -0
  214. fast_agent/resources/examples/workflows/parallel.py +56 -0
  215. fast_agent/resources/examples/workflows/router.py +69 -0
  216. fast_agent/resources/examples/workflows/short_story.md +13 -0
  217. fast_agent/resources/examples/workflows/short_story.txt +19 -0
  218. fast_agent/resources/setup/.gitignore +30 -0
  219. fast_agent/resources/setup/agent.py +28 -0
  220. fast_agent/resources/setup/fastagent.config.yaml +65 -0
  221. fast_agent/resources/setup/fastagent.secrets.yaml.example +38 -0
  222. fast_agent/resources/setup/pyproject.toml.tmpl +23 -0
  223. fast_agent/skills/__init__.py +9 -0
  224. fast_agent/skills/registry.py +235 -0
  225. fast_agent/tools/elicitation.py +369 -0
  226. fast_agent/tools/shell_runtime.py +402 -0
  227. fast_agent/types/__init__.py +59 -0
  228. fast_agent/types/conversation_summary.py +294 -0
  229. fast_agent/types/llm_stop_reason.py +78 -0
  230. fast_agent/types/message_search.py +249 -0
  231. fast_agent/ui/__init__.py +38 -0
  232. fast_agent/ui/console.py +59 -0
  233. fast_agent/ui/console_display.py +1080 -0
  234. fast_agent/ui/elicitation_form.py +946 -0
  235. fast_agent/ui/elicitation_style.py +59 -0
  236. fast_agent/ui/enhanced_prompt.py +1400 -0
  237. fast_agent/ui/history_display.py +734 -0
  238. fast_agent/ui/interactive_prompt.py +1199 -0
  239. fast_agent/ui/markdown_helpers.py +104 -0
  240. fast_agent/ui/markdown_truncator.py +1004 -0
  241. fast_agent/ui/mcp_display.py +857 -0
  242. fast_agent/ui/mcp_ui_utils.py +235 -0
  243. fast_agent/ui/mermaid_utils.py +169 -0
  244. fast_agent/ui/message_primitives.py +50 -0
  245. fast_agent/ui/notification_tracker.py +205 -0
  246. fast_agent/ui/plain_text_truncator.py +68 -0
  247. fast_agent/ui/progress_display.py +10 -0
  248. fast_agent/ui/rich_progress.py +195 -0
  249. fast_agent/ui/streaming.py +774 -0
  250. fast_agent/ui/streaming_buffer.py +449 -0
  251. fast_agent/ui/tool_display.py +422 -0
  252. fast_agent/ui/usage_display.py +204 -0
  253. fast_agent/utils/__init__.py +5 -0
  254. fast_agent/utils/reasoning_stream_parser.py +77 -0
  255. fast_agent/utils/time.py +22 -0
  256. fast_agent/workflow_telemetry.py +261 -0
  257. fast_agent_mcp-0.4.7.dist-info/METADATA +788 -0
  258. fast_agent_mcp-0.4.7.dist-info/RECORD +261 -0
  259. fast_agent_mcp-0.4.7.dist-info/WHEEL +4 -0
  260. fast_agent_mcp-0.4.7.dist-info/entry_points.txt +7 -0
  261. fast_agent_mcp-0.4.7.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,41 @@
1
+ """
2
+ Global constants for fast_agent with minimal dependencies to avoid circular imports.
3
+ """
4
+
5
+ # Canonical tool name for the human input/elicitation tool
6
+ HUMAN_INPUT_TOOL_NAME = "__human_input"
7
+ MCP_UI = "mcp-ui"
8
+ REASONING = "reasoning"
9
+ FAST_AGENT_ERROR_CHANNEL = "fast-agent-error"
10
+ FAST_AGENT_REMOVED_METADATA_CHANNEL = "fast-agent-removed-meta"
11
+ FAST_AGENT_TIMING = "fast-agent-timing"
12
+ FAST_AGENT_TOOL_TIMING = "fast-agent-tool-timing"
13
+ # should we have MAX_TOOL_CALLS instead to constrain by number of tools rather than turns...?
14
+ DEFAULT_MAX_ITERATIONS = 99
15
+ """Maximum number of User/Assistant turns to take"""
16
+
17
+ DEFAULT_TERMINAL_OUTPUT_BYTE_LIMIT = 8192
18
+ """Baseline byte limit for ACP terminal output when no model info exists."""
19
+
20
+ TERMINAL_OUTPUT_TOKEN_RATIO = 0.25
21
+ """Target fraction of model max output tokens to budget for terminal output."""
22
+
23
+ TERMINAL_OUTPUT_TOKEN_HEADROOM_RATIO = 0.2
24
+ """Leave headroom for tool wrapper text and other turn data."""
25
+
26
+ TERMINAL_AVG_BYTES_PER_TOKEN = 4
27
+ """Conservative bytes-per-token estimate for mapping token budgets to byte limits."""
28
+
29
+ MAX_TERMINAL_OUTPUT_BYTE_LIMIT = 32768
30
+ """Hard cap on default ACP terminal output to avoid oversized tool payloads."""
31
+
32
+ DEFAULT_AGENT_INSTRUCTION = """You are a helpful AI Agent.
33
+
34
+ {{serverInstructions}}
35
+ {{agentSkills}}
36
+ {{file_silent:AGENTS.md}}
37
+ {{env}}
38
+
39
+ The current date is {{currentDate}}."""
40
+
41
+ CONTROL_MESSAGE_SAVE_HISTORY = "***SAVE_HISTORY"
fast_agent/context.py ADDED
@@ -0,0 +1,279 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import concurrent.futures
5
+ import logging
6
+ import uuid
7
+ from os import PathLike
8
+ from pathlib import Path
9
+ from typing import TYPE_CHECKING, Any
10
+
11
+ from opentelemetry import trace
12
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
13
+
14
+ # from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor
15
+ from opentelemetry.propagate import set_global_textmap
16
+ from opentelemetry.sdk.resources import Resource
17
+ from opentelemetry.sdk.trace import TracerProvider
18
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
19
+ from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
20
+ from pydantic import BaseModel, ConfigDict
21
+
22
+ from fast_agent.config import Settings, get_settings
23
+ from fast_agent.core.executor.executor import AsyncioExecutor, Executor
24
+ from fast_agent.core.executor.task_registry import ActivityRegistry
25
+ from fast_agent.core.logging.events import EventFilter, StreamingExclusionFilter
26
+ from fast_agent.core.logging.logger import LoggingConfig, get_logger
27
+ from fast_agent.core.logging.transport import create_transport
28
+ from fast_agent.mcp_server_registry import ServerRegistry
29
+ from fast_agent.skills import SkillRegistry
30
+
31
+ if TYPE_CHECKING:
32
+ from fast_agent.acp.acp_context import ACPContext
33
+ from fast_agent.core.executor.workflow_signal import SignalWaitCallback
34
+ from fast_agent.mcp.mcp_connection_manager import MCPConnectionManager
35
+ else:
36
+ # Runtime placeholders for the types
37
+ ACPContext = Any
38
+ SignalWaitCallback = Any
39
+ MCPConnectionManager = Any
40
+
41
+ logger = get_logger(__name__)
42
+
43
+ """
44
+ A central context object to store global state that is shared across the application.
45
+ """
46
+
47
+
48
+ class Context(BaseModel):
49
+ """
50
+ Context that is passed around through the application.
51
+ This is a global context that is shared across the application.
52
+ """
53
+
54
+ config: Settings | None = None
55
+ executor: Executor | None = None
56
+ human_input_handler: Any | None = None
57
+ signal_notification: SignalWaitCallback | None = None
58
+
59
+ # Registries
60
+ server_registry: ServerRegistry | None = None
61
+ task_registry: ActivityRegistry | None = None
62
+ skill_registry: SkillRegistry | None = None
63
+
64
+ tracer: trace.Tracer | None = None
65
+ _connection_manager: "MCPConnectionManager | None" = None
66
+
67
+ # ACP context - set when running in ACP mode
68
+ # Provides agents access to ACP capabilities (mode switching, commands, etc.)
69
+ acp: "ACPContext | None" = None
70
+
71
+ model_config = ConfigDict(
72
+ extra="allow",
73
+ arbitrary_types_allowed=True, # Tell Pydantic to defer type evaluation
74
+ )
75
+
76
+
77
+ async def configure_otel(config: "Settings") -> None:
78
+ """
79
+ Configure OpenTelemetry based on the application config.
80
+ """
81
+ if not config.otel or not config.otel.enabled:
82
+ return
83
+
84
+ # Set up global textmap propagator first
85
+ set_global_textmap(TraceContextTextMapPropagator())
86
+
87
+ service_name = config.otel.service_name
88
+ from importlib.metadata import version
89
+
90
+ try:
91
+ app_version = version("fast-agent-mcp")
92
+ except: # noqa: E722
93
+ app_version = "unknown"
94
+
95
+ resource = Resource.create(
96
+ attributes={
97
+ key: value
98
+ for key, value in {
99
+ "service.name": service_name,
100
+ "service.instance.id": str(uuid.uuid4())[:6],
101
+ "service.version": app_version,
102
+ }.items()
103
+ if value is not None
104
+ }
105
+ )
106
+
107
+ # Create provider with resource
108
+ tracer_provider = TracerProvider(resource=resource)
109
+
110
+ # Add exporters based on config
111
+ otlp_endpoint = config.otel.otlp_endpoint
112
+ if otlp_endpoint:
113
+ exporter = OTLPSpanExporter(endpoint=otlp_endpoint)
114
+ tracer_provider.add_span_processor(BatchSpanProcessor(exporter))
115
+
116
+ if config.otel.console_debug:
117
+ tracer_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
118
+ else:
119
+ # Default to console exporter in development
120
+ tracer_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
121
+
122
+ # Set as global tracer provider
123
+ trace.set_tracer_provider(tracer_provider)
124
+
125
+ # Attempt to instrument optional SDKs if available; continue silently if missing
126
+ try:
127
+ from opentelemetry.instrumentation.openai import OpenAIInstrumentor
128
+
129
+ OpenAIInstrumentor().instrument()
130
+ except Exception: # pragma: no cover - optional instrumentation
131
+ pass
132
+
133
+ try:
134
+ from opentelemetry.instrumentation.google_genai import GoogleGenAiSdkInstrumentor
135
+
136
+ GoogleGenAiSdkInstrumentor().instrument()
137
+ except Exception: # pragma: no cover - optional instrumentation
138
+ pass
139
+
140
+
141
+ # McpInstrumentor().instrument()
142
+
143
+
144
+ async def configure_logger(config: "Settings") -> None:
145
+ """
146
+ Configure logging and tracing based on the application config.
147
+ """
148
+ settings = config.logger
149
+
150
+ # Configure the standard Python logger used by LoggingListener so it respects settings.
151
+ python_logger = logging.getLogger("fast_agent")
152
+ python_logger.handlers.clear()
153
+ python_logger.setLevel(settings.level.upper())
154
+ python_logger.propagate = False
155
+
156
+ transport = None
157
+ if settings.type == "console":
158
+ # Console mode: use the Python logger to emit to stdout and skip additional transport output
159
+ handler = logging.StreamHandler()
160
+ handler.setLevel(settings.level.upper())
161
+ handler.setFormatter(logging.Formatter("%(message)s"))
162
+ python_logger.addHandler(handler)
163
+ else:
164
+ # For all other modes, rely on transports (file/http/none) and keep the Python logger quiet
165
+ python_logger.addHandler(logging.NullHandler())
166
+
167
+ # Use StreamingExclusionFilter to prevent streaming events from flooding logs
168
+ event_filter: EventFilter = StreamingExclusionFilter(min_level=settings.level)
169
+ logger.info(f"Configuring logger with level: {settings.level}")
170
+ if settings.type == "console":
171
+ from fast_agent.core.logging.transport import NoOpTransport
172
+
173
+ transport = NoOpTransport(event_filter=event_filter)
174
+ else:
175
+ transport = create_transport(settings=settings, event_filter=event_filter)
176
+ await LoggingConfig.configure(
177
+ event_filter=event_filter,
178
+ transport=transport,
179
+ batch_size=settings.batch_size,
180
+ flush_interval=settings.flush_interval,
181
+ progress_display=settings.progress_display,
182
+ )
183
+
184
+
185
+ async def configure_executor(config: "Settings"):
186
+ """
187
+ Configure the executor based on the application config.
188
+ """
189
+ return AsyncioExecutor()
190
+
191
+
192
+ async def initialize_context(
193
+ config: Settings | str | PathLike[str] | None = None, store_globally: bool = False
194
+ ):
195
+ """
196
+ Initialize the global application context.
197
+ """
198
+ if config is None:
199
+ config = get_settings()
200
+ elif isinstance(config, (str, PathLike)):
201
+ # Accept pathlib.Path and other path-like objects for convenience in tests
202
+ config = get_settings(config_path=str(config))
203
+
204
+ context = Context()
205
+ context.config = config
206
+ context.server_registry = ServerRegistry(config=config)
207
+
208
+ skills_settings = getattr(config, "skills", None)
209
+ override_directory = None
210
+ if skills_settings and getattr(skills_settings, "directory", None):
211
+ override_directory = Path(skills_settings.directory).expanduser()
212
+ context.skill_registry = SkillRegistry(
213
+ base_dir=Path.cwd(),
214
+ override_directory=override_directory,
215
+ )
216
+
217
+ # Configure logging and telemetry
218
+ await configure_otel(config)
219
+ await configure_logger(config)
220
+
221
+ # Configure the executor
222
+ context.executor = await configure_executor(config)
223
+ context.task_registry = ActivityRegistry()
224
+
225
+ # Store the tracer in context if needed
226
+ if config.otel:
227
+ context.tracer = trace.get_tracer(config.otel.service_name)
228
+
229
+ if store_globally:
230
+ global _global_context
231
+ _global_context = context
232
+
233
+ return context
234
+
235
+
236
+ async def cleanup_context() -> None:
237
+ """
238
+ Cleanup the global application context.
239
+ """
240
+
241
+ # Shutdown logging and telemetry
242
+ await LoggingConfig.shutdown()
243
+
244
+
245
+ _global_context: Context | None = None
246
+
247
+
248
+ def get_current_context() -> Context:
249
+ """
250
+ Synchronous initializer/getter for global application context.
251
+ """
252
+ global _global_context
253
+ if _global_context is None:
254
+ try:
255
+ # Try to get the current event loop
256
+ loop = asyncio.get_event_loop()
257
+ if loop.is_running():
258
+ # Create a new loop in a separate thread
259
+ def run_async():
260
+ new_loop = asyncio.new_event_loop()
261
+ asyncio.set_event_loop(new_loop)
262
+ return new_loop.run_until_complete(initialize_context())
263
+
264
+ with concurrent.futures.ThreadPoolExecutor() as pool:
265
+ _global_context = pool.submit(run_async).result()
266
+ else:
267
+ _global_context = loop.run_until_complete(initialize_context())
268
+ except RuntimeError:
269
+ _global_context = asyncio.run(initialize_context())
270
+ return _global_context
271
+
272
+
273
+ def get_current_config():
274
+ """
275
+ Get the current application config.
276
+
277
+ Returns the context config if available, otherwise falls back to global settings.
278
+ """
279
+ return get_current_context().config or get_settings()
@@ -0,0 +1,50 @@
1
+ from contextlib import contextmanager
2
+ from typing import TYPE_CHECKING, Any
3
+
4
+ if TYPE_CHECKING:
5
+ from fast_agent.context import Context
6
+
7
+
8
+ class ContextDependent:
9
+ """
10
+ Mixin class for components that need context access.
11
+ Provides both global fallback and instance-specific context support.
12
+ """
13
+
14
+ # Ensure the attribute always exists even if a subclass
15
+ # does not call this mixin's __init__.
16
+ _context: "Context | None" = None
17
+
18
+ def __init__(self, context: "Context | None" = None, **kwargs: dict[str, Any]) -> None:
19
+ self._context = context
20
+ super().__init__()
21
+
22
+ @property
23
+ def context(self) -> "Context":
24
+ """
25
+ Get context, with graceful fallback to global context if needed.
26
+ Raises clear error if no context is available.
27
+ """
28
+ # First try instance context
29
+ if self._context is not None:
30
+ return self._context
31
+
32
+ try:
33
+ # Fall back to global context if available
34
+ from fast_agent.context import get_current_context
35
+
36
+ return get_current_context()
37
+ except Exception as e:
38
+ raise RuntimeError(
39
+ f"No context available for {self.__class__.__name__}. Either initialize Core first or pass context explicitly."
40
+ ) from e
41
+
42
+ @contextmanager
43
+ def use_context(self, context: "Context"):
44
+ """Temporarily use a different context."""
45
+ old_context = self._context
46
+ self._context = context
47
+ try:
48
+ yield
49
+ finally:
50
+ self._context = old_context
@@ -0,0 +1,92 @@
1
+ """
2
+ Core interfaces and decorators for fast-agent.
3
+
4
+ Public API:
5
+ - `Core`: The core application container
6
+ - `AgentApp`: Container for interacting with agents
7
+ - `FastAgent`: High-level, decorator-driven application class
8
+ - Decorators: `agent`, `custom`, `orchestrator`, `iterative_planner`,
9
+ `router`, `chain`, `parallel`, `evaluator_optimizer`
10
+
11
+ Exports are resolved lazily to avoid circular imports during package init.
12
+ """
13
+
14
+ from typing import TYPE_CHECKING
15
+
16
+
17
+ def __getattr__(name: str):
18
+ if name == "AgentApp":
19
+ from .agent_app import AgentApp
20
+
21
+ return AgentApp
22
+ elif name == "Core":
23
+ from .core_app import Core
24
+
25
+ return Core
26
+ elif name == "FastAgent":
27
+ from .fastagent import FastAgent
28
+
29
+ return FastAgent
30
+ elif name in (
31
+ "agent",
32
+ "custom",
33
+ "orchestrator",
34
+ "iterative_planner",
35
+ "router",
36
+ "chain",
37
+ "parallel",
38
+ "evaluator_optimizer",
39
+ ):
40
+ from . import direct_decorators as _dd
41
+
42
+ return getattr(
43
+ _dd,
44
+ name,
45
+ )
46
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
47
+
48
+
49
+ if TYPE_CHECKING: # pragma: no cover - typing aid only
50
+ from .agent_app import AgentApp as AgentApp # noqa: F401
51
+ from .core_app import Core as Core # noqa: F401
52
+ from .direct_decorators import ( # noqa: F401
53
+ agent as agent,
54
+ )
55
+ from .direct_decorators import (
56
+ chain as chain,
57
+ )
58
+ from .direct_decorators import (
59
+ custom as custom,
60
+ )
61
+ from .direct_decorators import (
62
+ evaluator_optimizer as evaluator_optimizer,
63
+ )
64
+ from .direct_decorators import (
65
+ iterative_planner as iterative_planner,
66
+ )
67
+ from .direct_decorators import (
68
+ orchestrator as orchestrator,
69
+ )
70
+ from .direct_decorators import (
71
+ parallel as parallel,
72
+ )
73
+ from .direct_decorators import (
74
+ router as router,
75
+ )
76
+ from .fastagent import FastAgent as FastAgent # noqa: F401
77
+
78
+
79
+ __all__ = [
80
+ "Core",
81
+ "AgentApp",
82
+ "FastAgent",
83
+ # Decorators
84
+ "agent",
85
+ "custom",
86
+ "orchestrator",
87
+ "iterative_planner",
88
+ "router",
89
+ "chain",
90
+ "parallel",
91
+ "evaluator_optimizer",
92
+ ]