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,278 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Logger module for the MCP Agent, which provides:
|
|
3
|
+
- Local + optional remote event transport
|
|
4
|
+
- Async event bus
|
|
5
|
+
- OpenTelemetry tracing decorators (for distributed tracing)
|
|
6
|
+
- Automatic injection of trace_id/span_id into events
|
|
7
|
+
- Developer-friendly Logger that can be used anywhere
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
import logging
|
|
12
|
+
import threading
|
|
13
|
+
import time
|
|
14
|
+
from contextlib import asynccontextmanager, contextmanager
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from fast_agent.core.logging.events import Event, EventContext, EventFilter, EventType
|
|
18
|
+
from fast_agent.core.logging.listeners import (
|
|
19
|
+
BatchingListener,
|
|
20
|
+
LoggingListener,
|
|
21
|
+
ProgressListener,
|
|
22
|
+
)
|
|
23
|
+
from fast_agent.core.logging.transport import AsyncEventBus, EventTransport
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Logger:
|
|
27
|
+
"""
|
|
28
|
+
Developer-friendly logger that sends events to the AsyncEventBus.
|
|
29
|
+
- `type` is a broad category (INFO, ERROR, etc.).
|
|
30
|
+
- `name` can be a custom domain-specific event name, e.g. "ORDER_PLACED".
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, namespace: str) -> None:
|
|
34
|
+
self.namespace = namespace
|
|
35
|
+
self.event_bus = AsyncEventBus.get()
|
|
36
|
+
|
|
37
|
+
def _ensure_event_loop(self):
|
|
38
|
+
"""Ensure we have an event loop we can use."""
|
|
39
|
+
try:
|
|
40
|
+
return asyncio.get_running_loop()
|
|
41
|
+
except RuntimeError:
|
|
42
|
+
# If no loop is running, create a new one
|
|
43
|
+
loop = asyncio.new_event_loop()
|
|
44
|
+
asyncio.set_event_loop(loop)
|
|
45
|
+
return loop
|
|
46
|
+
|
|
47
|
+
def _emit_event(self, event: Event) -> None:
|
|
48
|
+
"""Emit an event by running it in the event loop."""
|
|
49
|
+
loop = self._ensure_event_loop()
|
|
50
|
+
if loop.is_running():
|
|
51
|
+
# If we're in a thread with a running loop, schedule the coroutine
|
|
52
|
+
asyncio.create_task(self.event_bus.emit(event))
|
|
53
|
+
else:
|
|
54
|
+
# If no loop is running, run it until the emit completes
|
|
55
|
+
loop.run_until_complete(self.event_bus.emit(event))
|
|
56
|
+
|
|
57
|
+
def event(
|
|
58
|
+
self,
|
|
59
|
+
etype: EventType,
|
|
60
|
+
ename: str | None,
|
|
61
|
+
message: str,
|
|
62
|
+
context: EventContext | None,
|
|
63
|
+
data: dict,
|
|
64
|
+
) -> None:
|
|
65
|
+
"""Create and emit an event."""
|
|
66
|
+
evt = Event(
|
|
67
|
+
type=etype,
|
|
68
|
+
name=ename,
|
|
69
|
+
namespace=self.namespace,
|
|
70
|
+
message=message,
|
|
71
|
+
context=context,
|
|
72
|
+
data=data,
|
|
73
|
+
)
|
|
74
|
+
self._emit_event(evt)
|
|
75
|
+
|
|
76
|
+
def debug(
|
|
77
|
+
self,
|
|
78
|
+
message: str,
|
|
79
|
+
name: str | None = None,
|
|
80
|
+
context: EventContext | None = None,
|
|
81
|
+
**data,
|
|
82
|
+
) -> None:
|
|
83
|
+
"""Log a debug message."""
|
|
84
|
+
self.event("debug", name, message, context, data)
|
|
85
|
+
|
|
86
|
+
def info(
|
|
87
|
+
self,
|
|
88
|
+
message: str,
|
|
89
|
+
name: str | None = None,
|
|
90
|
+
context: EventContext | None = None,
|
|
91
|
+
**data,
|
|
92
|
+
) -> None:
|
|
93
|
+
"""Log an info message."""
|
|
94
|
+
self.event("info", name, message, context, data)
|
|
95
|
+
|
|
96
|
+
def warning(
|
|
97
|
+
self,
|
|
98
|
+
message: str,
|
|
99
|
+
name: str | None = None,
|
|
100
|
+
context: EventContext | None = None,
|
|
101
|
+
**data,
|
|
102
|
+
) -> None:
|
|
103
|
+
"""Log a warning message."""
|
|
104
|
+
self.event("warning", name, message, context, data)
|
|
105
|
+
|
|
106
|
+
def error(
|
|
107
|
+
self,
|
|
108
|
+
message: str,
|
|
109
|
+
name: str | None = None,
|
|
110
|
+
context: EventContext | None = None,
|
|
111
|
+
**data,
|
|
112
|
+
) -> None:
|
|
113
|
+
"""Log an error message."""
|
|
114
|
+
self.event("error", name, message, context, data)
|
|
115
|
+
|
|
116
|
+
def progress(
|
|
117
|
+
self,
|
|
118
|
+
message: str,
|
|
119
|
+
name: str | None = None,
|
|
120
|
+
percentage: float | None = None,
|
|
121
|
+
context: EventContext | None = None,
|
|
122
|
+
**data,
|
|
123
|
+
) -> None:
|
|
124
|
+
"""Log a progress message."""
|
|
125
|
+
merged_data = dict(percentage=percentage, **data)
|
|
126
|
+
self.event("progress", name, message, context, merged_data)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@contextmanager
|
|
130
|
+
def event_context(
|
|
131
|
+
logger: Logger,
|
|
132
|
+
message: str,
|
|
133
|
+
event_type: EventType = "info",
|
|
134
|
+
name: str | None = None,
|
|
135
|
+
**data,
|
|
136
|
+
):
|
|
137
|
+
"""
|
|
138
|
+
Times a synchronous block, logs an event after completion.
|
|
139
|
+
Because logger methods are async, we schedule the final log.
|
|
140
|
+
"""
|
|
141
|
+
start_time = time.time()
|
|
142
|
+
try:
|
|
143
|
+
yield
|
|
144
|
+
finally:
|
|
145
|
+
duration = time.time() - start_time
|
|
146
|
+
|
|
147
|
+
logger.event(
|
|
148
|
+
event_type,
|
|
149
|
+
name,
|
|
150
|
+
f"{message} finished in {duration:.3f}s",
|
|
151
|
+
None,
|
|
152
|
+
{"duration": duration, **data},
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# TODO: saqadri - check if we need this
|
|
157
|
+
@asynccontextmanager
|
|
158
|
+
async def async_event_context(
|
|
159
|
+
logger: Logger,
|
|
160
|
+
message: str,
|
|
161
|
+
event_type: EventType = "info",
|
|
162
|
+
name: str | None = None,
|
|
163
|
+
**data,
|
|
164
|
+
):
|
|
165
|
+
"""
|
|
166
|
+
Times an asynchronous block, logs an event after completion.
|
|
167
|
+
Because logger methods are async, we schedule the final log.
|
|
168
|
+
"""
|
|
169
|
+
start_time = time.time()
|
|
170
|
+
try:
|
|
171
|
+
yield
|
|
172
|
+
finally:
|
|
173
|
+
duration = time.time() - start_time
|
|
174
|
+
logger.event(
|
|
175
|
+
event_type,
|
|
176
|
+
name,
|
|
177
|
+
f"{message} finished in {duration:.3f}s",
|
|
178
|
+
None,
|
|
179
|
+
{"duration": duration, **data},
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class LoggingConfig:
|
|
184
|
+
"""Global configuration for the logging system."""
|
|
185
|
+
|
|
186
|
+
_initialized = False
|
|
187
|
+
|
|
188
|
+
@classmethod
|
|
189
|
+
async def configure(
|
|
190
|
+
cls,
|
|
191
|
+
event_filter: EventFilter | None = None,
|
|
192
|
+
transport: EventTransport | None = None,
|
|
193
|
+
batch_size: int = 100,
|
|
194
|
+
flush_interval: float = 2.0,
|
|
195
|
+
**kwargs: Any,
|
|
196
|
+
) -> None:
|
|
197
|
+
"""
|
|
198
|
+
Configure the logging system.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
event_filter: Default filter for all loggers
|
|
202
|
+
transport: Transport for sending events to external systems
|
|
203
|
+
batch_size: Default batch size for batching listener
|
|
204
|
+
flush_interval: Default flush interval for batching listener
|
|
205
|
+
**kwargs: Additional configuration options
|
|
206
|
+
"""
|
|
207
|
+
if cls._initialized:
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
# Suppress boto3/botocore logging to prevent flooding
|
|
211
|
+
logging.getLogger("boto3").setLevel(logging.WARNING)
|
|
212
|
+
logging.getLogger("botocore").setLevel(logging.WARNING)
|
|
213
|
+
logging.getLogger("urllib3").setLevel(logging.WARNING)
|
|
214
|
+
logging.getLogger("s3transfer").setLevel(logging.WARNING)
|
|
215
|
+
|
|
216
|
+
bus = AsyncEventBus.get(transport=transport)
|
|
217
|
+
|
|
218
|
+
# Add standard listeners
|
|
219
|
+
if "logging" not in bus.listeners:
|
|
220
|
+
bus.add_listener("logging", LoggingListener(event_filter=event_filter))
|
|
221
|
+
|
|
222
|
+
# Only add progress listener if enabled in settings
|
|
223
|
+
if "progress" not in bus.listeners and kwargs.get("progress_display", True):
|
|
224
|
+
bus.add_listener("progress", ProgressListener())
|
|
225
|
+
|
|
226
|
+
if "batching" not in bus.listeners:
|
|
227
|
+
bus.add_listener(
|
|
228
|
+
"batching",
|
|
229
|
+
BatchingListener(
|
|
230
|
+
event_filter=event_filter,
|
|
231
|
+
batch_size=batch_size,
|
|
232
|
+
flush_interval=flush_interval,
|
|
233
|
+
),
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
await bus.start()
|
|
237
|
+
cls._initialized = True
|
|
238
|
+
|
|
239
|
+
@classmethod
|
|
240
|
+
async def shutdown(cls) -> None:
|
|
241
|
+
"""Shutdown the logging system gracefully."""
|
|
242
|
+
if not cls._initialized:
|
|
243
|
+
return
|
|
244
|
+
bus = AsyncEventBus.get()
|
|
245
|
+
await bus.stop()
|
|
246
|
+
cls._initialized = False
|
|
247
|
+
|
|
248
|
+
@classmethod
|
|
249
|
+
@asynccontextmanager
|
|
250
|
+
async def managed(cls, **config_kwargs):
|
|
251
|
+
"""Context manager for the logging system lifecycle."""
|
|
252
|
+
try:
|
|
253
|
+
await cls.configure(**config_kwargs)
|
|
254
|
+
yield
|
|
255
|
+
finally:
|
|
256
|
+
await cls.shutdown()
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
_logger_lock = threading.Lock()
|
|
260
|
+
_loggers: dict[str, Logger] = {}
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def get_logger(namespace: str) -> Logger:
|
|
264
|
+
"""
|
|
265
|
+
Get a logger instance for a given namespace.
|
|
266
|
+
Creates a new logger if one doesn't exist for this namespace.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
namespace: The namespace for the logger (e.g. "agent.helper", "workflow.demo")
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
A Logger instance for the given namespace
|
|
273
|
+
"""
|
|
274
|
+
|
|
275
|
+
with _logger_lock:
|
|
276
|
+
if namespace not in _loggers:
|
|
277
|
+
_loggers[namespace] = Logger(namespace)
|
|
278
|
+
return _loggers[namespace]
|