agentscope-runtime 0.1.0__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 (131) hide show
  1. agentscope_runtime/__init__.py +4 -0
  2. agentscope_runtime/engine/__init__.py +9 -0
  3. agentscope_runtime/engine/agents/__init__.py +2 -0
  4. agentscope_runtime/engine/agents/agentscope_agent/__init__.py +6 -0
  5. agentscope_runtime/engine/agents/agentscope_agent/agent.py +342 -0
  6. agentscope_runtime/engine/agents/agentscope_agent/hooks.py +156 -0
  7. agentscope_runtime/engine/agents/agno_agent.py +220 -0
  8. agentscope_runtime/engine/agents/base_agent.py +29 -0
  9. agentscope_runtime/engine/agents/langgraph_agent.py +59 -0
  10. agentscope_runtime/engine/agents/llm_agent.py +51 -0
  11. agentscope_runtime/engine/deployers/__init__.py +3 -0
  12. agentscope_runtime/engine/deployers/adapter/__init__.py +0 -0
  13. agentscope_runtime/engine/deployers/adapter/a2a/__init__.py +2 -0
  14. agentscope_runtime/engine/deployers/adapter/a2a/a2a_adapter_utils.py +425 -0
  15. agentscope_runtime/engine/deployers/adapter/a2a/a2a_agent_adapter.py +69 -0
  16. agentscope_runtime/engine/deployers/adapter/a2a/a2a_protocol_adapter.py +60 -0
  17. agentscope_runtime/engine/deployers/adapter/protocol_adapter.py +24 -0
  18. agentscope_runtime/engine/deployers/base.py +17 -0
  19. agentscope_runtime/engine/deployers/local_deployer.py +586 -0
  20. agentscope_runtime/engine/helpers/helper.py +127 -0
  21. agentscope_runtime/engine/llms/__init__.py +3 -0
  22. agentscope_runtime/engine/llms/base_llm.py +60 -0
  23. agentscope_runtime/engine/llms/qwen_llm.py +47 -0
  24. agentscope_runtime/engine/misc/__init__.py +0 -0
  25. agentscope_runtime/engine/runner.py +186 -0
  26. agentscope_runtime/engine/schemas/__init__.py +0 -0
  27. agentscope_runtime/engine/schemas/agent_schemas.py +551 -0
  28. agentscope_runtime/engine/schemas/context.py +54 -0
  29. agentscope_runtime/engine/services/__init__.py +9 -0
  30. agentscope_runtime/engine/services/base.py +77 -0
  31. agentscope_runtime/engine/services/context_manager.py +129 -0
  32. agentscope_runtime/engine/services/environment_manager.py +50 -0
  33. agentscope_runtime/engine/services/manager.py +174 -0
  34. agentscope_runtime/engine/services/memory_service.py +270 -0
  35. agentscope_runtime/engine/services/sandbox_service.py +198 -0
  36. agentscope_runtime/engine/services/session_history_service.py +256 -0
  37. agentscope_runtime/engine/tracing/__init__.py +40 -0
  38. agentscope_runtime/engine/tracing/base.py +309 -0
  39. agentscope_runtime/engine/tracing/local_logging_handler.py +356 -0
  40. agentscope_runtime/engine/tracing/tracing_metric.py +69 -0
  41. agentscope_runtime/engine/tracing/wrapper.py +321 -0
  42. agentscope_runtime/sandbox/__init__.py +14 -0
  43. agentscope_runtime/sandbox/box/__init__.py +0 -0
  44. agentscope_runtime/sandbox/box/base/__init__.py +0 -0
  45. agentscope_runtime/sandbox/box/base/base_sandbox.py +37 -0
  46. agentscope_runtime/sandbox/box/base/box/__init__.py +0 -0
  47. agentscope_runtime/sandbox/box/browser/__init__.py +0 -0
  48. agentscope_runtime/sandbox/box/browser/box/__init__.py +0 -0
  49. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +176 -0
  50. agentscope_runtime/sandbox/box/dummy/__init__.py +0 -0
  51. agentscope_runtime/sandbox/box/dummy/dummy_sandbox.py +26 -0
  52. agentscope_runtime/sandbox/box/filesystem/__init__.py +0 -0
  53. agentscope_runtime/sandbox/box/filesystem/box/__init__.py +0 -0
  54. agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +87 -0
  55. agentscope_runtime/sandbox/box/sandbox.py +115 -0
  56. agentscope_runtime/sandbox/box/shared/__init__.py +0 -0
  57. agentscope_runtime/sandbox/box/shared/app.py +44 -0
  58. agentscope_runtime/sandbox/box/shared/dependencies/__init__.py +5 -0
  59. agentscope_runtime/sandbox/box/shared/dependencies/deps.py +22 -0
  60. agentscope_runtime/sandbox/box/shared/routers/__init__.py +12 -0
  61. agentscope_runtime/sandbox/box/shared/routers/generic.py +173 -0
  62. agentscope_runtime/sandbox/box/shared/routers/mcp.py +207 -0
  63. agentscope_runtime/sandbox/box/shared/routers/mcp_utils.py +153 -0
  64. agentscope_runtime/sandbox/box/shared/routers/runtime_watcher.py +187 -0
  65. agentscope_runtime/sandbox/box/shared/routers/workspace.py +325 -0
  66. agentscope_runtime/sandbox/box/training_box/__init__.py +0 -0
  67. agentscope_runtime/sandbox/box/training_box/base.py +120 -0
  68. agentscope_runtime/sandbox/box/training_box/env_service.py +752 -0
  69. agentscope_runtime/sandbox/box/training_box/environments/__init__.py +0 -0
  70. agentscope_runtime/sandbox/box/training_box/environments/appworld/appworld_env.py +987 -0
  71. agentscope_runtime/sandbox/box/training_box/registry.py +54 -0
  72. agentscope_runtime/sandbox/box/training_box/src/trajectory.py +278 -0
  73. agentscope_runtime/sandbox/box/training_box/training_box.py +219 -0
  74. agentscope_runtime/sandbox/build.py +213 -0
  75. agentscope_runtime/sandbox/client/__init__.py +5 -0
  76. agentscope_runtime/sandbox/client/http_client.py +527 -0
  77. agentscope_runtime/sandbox/client/training_client.py +265 -0
  78. agentscope_runtime/sandbox/constant.py +5 -0
  79. agentscope_runtime/sandbox/custom/__init__.py +16 -0
  80. agentscope_runtime/sandbox/custom/custom_sandbox.py +40 -0
  81. agentscope_runtime/sandbox/custom/example.py +37 -0
  82. agentscope_runtime/sandbox/enums.py +68 -0
  83. agentscope_runtime/sandbox/manager/__init__.py +4 -0
  84. agentscope_runtime/sandbox/manager/collections/__init__.py +22 -0
  85. agentscope_runtime/sandbox/manager/collections/base_mapping.py +20 -0
  86. agentscope_runtime/sandbox/manager/collections/base_queue.py +25 -0
  87. agentscope_runtime/sandbox/manager/collections/base_set.py +25 -0
  88. agentscope_runtime/sandbox/manager/collections/in_memory_mapping.py +22 -0
  89. agentscope_runtime/sandbox/manager/collections/in_memory_queue.py +28 -0
  90. agentscope_runtime/sandbox/manager/collections/in_memory_set.py +27 -0
  91. agentscope_runtime/sandbox/manager/collections/redis_mapping.py +26 -0
  92. agentscope_runtime/sandbox/manager/collections/redis_queue.py +27 -0
  93. agentscope_runtime/sandbox/manager/collections/redis_set.py +23 -0
  94. agentscope_runtime/sandbox/manager/container_clients/__init__.py +8 -0
  95. agentscope_runtime/sandbox/manager/container_clients/base_client.py +39 -0
  96. agentscope_runtime/sandbox/manager/container_clients/docker_client.py +170 -0
  97. agentscope_runtime/sandbox/manager/sandbox_manager.py +694 -0
  98. agentscope_runtime/sandbox/manager/server/__init__.py +0 -0
  99. agentscope_runtime/sandbox/manager/server/app.py +194 -0
  100. agentscope_runtime/sandbox/manager/server/config.py +68 -0
  101. agentscope_runtime/sandbox/manager/server/models.py +17 -0
  102. agentscope_runtime/sandbox/manager/storage/__init__.py +10 -0
  103. agentscope_runtime/sandbox/manager/storage/data_storage.py +16 -0
  104. agentscope_runtime/sandbox/manager/storage/local_storage.py +44 -0
  105. agentscope_runtime/sandbox/manager/storage/oss_storage.py +89 -0
  106. agentscope_runtime/sandbox/manager/utils.py +78 -0
  107. agentscope_runtime/sandbox/mcp_server.py +192 -0
  108. agentscope_runtime/sandbox/model/__init__.py +12 -0
  109. agentscope_runtime/sandbox/model/api.py +16 -0
  110. agentscope_runtime/sandbox/model/container.py +72 -0
  111. agentscope_runtime/sandbox/model/manager_config.py +158 -0
  112. agentscope_runtime/sandbox/registry.py +129 -0
  113. agentscope_runtime/sandbox/tools/__init__.py +12 -0
  114. agentscope_runtime/sandbox/tools/base/__init__.py +8 -0
  115. agentscope_runtime/sandbox/tools/base/tool.py +52 -0
  116. agentscope_runtime/sandbox/tools/browser/__init__.py +57 -0
  117. agentscope_runtime/sandbox/tools/browser/tool.py +597 -0
  118. agentscope_runtime/sandbox/tools/filesystem/__init__.py +32 -0
  119. agentscope_runtime/sandbox/tools/filesystem/tool.py +319 -0
  120. agentscope_runtime/sandbox/tools/function_tool.py +321 -0
  121. agentscope_runtime/sandbox/tools/mcp_tool.py +191 -0
  122. agentscope_runtime/sandbox/tools/sandbox_tool.py +104 -0
  123. agentscope_runtime/sandbox/tools/tool.py +123 -0
  124. agentscope_runtime/sandbox/tools/utils.py +68 -0
  125. agentscope_runtime/version.py +2 -0
  126. agentscope_runtime-0.1.0.dist-info/METADATA +327 -0
  127. agentscope_runtime-0.1.0.dist-info/RECORD +131 -0
  128. agentscope_runtime-0.1.0.dist-info/WHEEL +5 -0
  129. agentscope_runtime-0.1.0.dist-info/entry_points.txt +4 -0
  130. agentscope_runtime-0.1.0.dist-info/licenses/LICENSE +202 -0
  131. agentscope_runtime-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,309 @@
1
+ # -*- coding: utf-8 -*-
2
+ # pylint: disable=protected-access
3
+ import time
4
+ import traceback
5
+ from abc import ABC, abstractmethod
6
+ from contextlib import contextmanager
7
+ from typing import Any, Dict, List
8
+
9
+ from .tracing_metric import TraceType
10
+
11
+
12
+ # Handler Interface
13
+ class TracerHandler(ABC):
14
+ """Abstract base class for tracer handlers."""
15
+
16
+ @abstractmethod
17
+ def on_start(
18
+ self,
19
+ event_type: TraceType,
20
+ payload: Dict[str, Any],
21
+ **kwargs: Any,
22
+ ) -> None:
23
+ """Handle the start of a trace event.
24
+
25
+ Args:
26
+ event_type (TraceType): The type of event being traced.
27
+ payload (Dict[str, Any]): The payload data for the event.
28
+ **kwargs (Any): Additional keyword arguments.
29
+ """
30
+
31
+ @abstractmethod
32
+ def on_end(
33
+ self,
34
+ event_type: TraceType,
35
+ start_payload: Dict[str, Any],
36
+ end_payload: Dict[str, Any],
37
+ start_time: float,
38
+ **kwargs: Any,
39
+ ) -> None:
40
+ """Handle the end of a trace event.
41
+
42
+ Args:
43
+ event_type (TraceType): The type of event being traced.
44
+ start_payload (Dict[str, Any]): The payload data from event start.
45
+ end_payload (Dict[str, Any]): The payload data from event end.
46
+ start_time (float): The timestamp when the event started.
47
+ **kwargs (Any): Additional keyword arguments.
48
+ """
49
+
50
+ @abstractmethod
51
+ def on_log(self, message: str, **kwargs: Any) -> None:
52
+ """Handle a log message during tracing.
53
+
54
+ Args:
55
+ message (str): The log message.
56
+ **kwargs (Any): Additional keyword arguments.
57
+ """
58
+
59
+ @abstractmethod
60
+ def on_error(
61
+ self,
62
+ event_type: TraceType,
63
+ start_payload: Dict[str, Any],
64
+ error: Exception,
65
+ start_time: float,
66
+ traceback_info: str,
67
+ **kwargs: Any,
68
+ ) -> None:
69
+ """Handle an error during tracing.
70
+
71
+ Args:
72
+ event_type (TraceType): The type of event being traced.
73
+ start_payload (Dict[str, Any]): The payload data from event start.
74
+ error (Exception): The exception that occurred.
75
+ start_time (float): The timestamp when the event started.
76
+ traceback_info (str): The traceback information.
77
+ **kwargs (Any): Additional keyword arguments.
78
+ """
79
+
80
+
81
+ class BaseLogHandler(TracerHandler):
82
+ """Basic log handler implementation using Python's logging module."""
83
+
84
+ import logging
85
+
86
+ logger = logging.getLogger(__name__)
87
+
88
+ def on_start(
89
+ self,
90
+ event_type: TraceType,
91
+ payload: Dict[str, Any],
92
+ **kwargs: Any,
93
+ ) -> None:
94
+ """Log the start of a trace event.
95
+
96
+ Args:
97
+ event_type (TraceType): The type of event being traced.
98
+ payload (Dict[str, Any]): The payload data for the event.
99
+ **kwargs (Any): Additional keyword arguments.
100
+ """
101
+ self.logger.info(f"Event {event_type} started with payload: {payload}")
102
+
103
+ def on_end(
104
+ self,
105
+ event_type: TraceType,
106
+ start_payload: Dict[str, Any],
107
+ end_payload: Dict[str, Any],
108
+ start_time: float,
109
+ **kwargs: Any,
110
+ ) -> None:
111
+ """Log the end of a trace event.
112
+
113
+ Args:
114
+ event_type (TraceType): The type of event being traced.
115
+ start_payload (Dict[str, Any]): The payload data from event start.
116
+ end_payload (Dict[str, Any]): The payload data from event end.
117
+ start_time (float): The timestamp when the event started.
118
+ **kwargs (Any): Additional keyword arguments.
119
+ """
120
+ self.logger.info(
121
+ f"Event {event_type} ended with start payload: {start_payload}, "
122
+ f"end payload: {end_payload}, duration: "
123
+ f"{time.time() - start_time} seconds, kwargs: {kwargs}",
124
+ )
125
+
126
+ def on_log(self, message: str, **kwargs: Any) -> None:
127
+ """Log a message during tracing.
128
+
129
+ Args:
130
+ message (str): The log message.
131
+ **kwargs (Any): Additional keyword arguments.
132
+ """
133
+ if message:
134
+ self.logger.info(f"Log: {message}")
135
+
136
+ def on_error(
137
+ self,
138
+ event_type: TraceType,
139
+ start_payload: Dict[str, Any],
140
+ error: Exception,
141
+ start_time: float,
142
+ traceback_info: str,
143
+ **kwargs: Any,
144
+ ) -> None:
145
+ """Log an error during tracing.
146
+
147
+ Args:
148
+ event_type (TraceType): The type of event being traced.
149
+ start_payload (Dict[str, Any]): The payload data from event start.
150
+ error (Exception): The exception that occurred.
151
+ start_time (float): The timestamp when the event started.
152
+ traceback_info (str): The traceback information.
153
+ **kwargs (Any): Additional keyword arguments.
154
+ """
155
+ self.logger.error(
156
+ f"Error in event {event_type} with payload: {start_payload}, "
157
+ f"error: {error}, "
158
+ f"traceback: {traceback_info}, duration: "
159
+ f"{time.time() - start_time} seconds, kwargs: {kwargs}",
160
+ )
161
+
162
+
163
+ class Tracer:
164
+ """
165
+ Tracer class for logging events
166
+ usage:
167
+ with tracer.event(TraceType.LLM, payload) as event:
168
+ event.log("message")
169
+ ""...logic here...""
170
+ end_payload = {xxx}
171
+ # optional on_end call for additional payload and kwargs
172
+ event.on_end(end_payload, if_success=True)
173
+ """
174
+
175
+ def __init__(self, handlers: List[TracerHandler]):
176
+ """Initialize the tracer with a list of handlers.
177
+
178
+ Args:
179
+ handlers (List[TracerHandler]): List of handlers to process trace
180
+ events.
181
+ """
182
+ self.handlers = handlers
183
+
184
+ @contextmanager
185
+ def event(
186
+ self,
187
+ event_type: TraceType,
188
+ payload: Dict[str, Any],
189
+ **kwargs: Any,
190
+ ) -> Any:
191
+ """Create a context manager for tracing an event.
192
+
193
+ Args:
194
+ event_type (TraceType): The type of event being traced.
195
+ payload (Dict[str, Any]): The payload data for the event.
196
+ **kwargs (Any): Additional keyword arguments.
197
+
198
+ Yields:
199
+ EventContext: The event context for logging and managing the trace.
200
+ """
201
+ start_time = time.time()
202
+
203
+ for handle in self.handlers:
204
+ handle.on_start(event_type, payload, **kwargs)
205
+
206
+ event_context = EventContext(
207
+ self.handlers,
208
+ event_type,
209
+ start_time,
210
+ payload,
211
+ )
212
+ try:
213
+ yield event_context
214
+ except Exception as e:
215
+ traceback_info = traceback.format_exc()
216
+ for handle in self.handlers:
217
+ handle.on_error(
218
+ event_type,
219
+ payload,
220
+ e,
221
+ start_time,
222
+ traceback_info=traceback_info,
223
+ )
224
+ raise
225
+ event_context._end(payload)
226
+
227
+ def log(self, message: str, **kwargs: Any) -> None:
228
+ """Log a message using all registered handlers.
229
+
230
+ Args:
231
+ message (str): The log message.
232
+ **kwargs (Any): Additional keyword arguments.
233
+ """
234
+ for handle in self.handlers:
235
+ handle.on_log(message, **kwargs)
236
+
237
+
238
+ class EventContext:
239
+ """Context manager for individual trace events."""
240
+
241
+ def __init__(
242
+ self,
243
+ handlers: List[TracerHandler],
244
+ event_type: TraceType,
245
+ start_time: float,
246
+ start_payload: Dict[str, Any],
247
+ ) -> None:
248
+ """Initialize the event context.
249
+
250
+ Args:
251
+ handlers (List[TracerHandler]): List of handlers to process
252
+ trace events.
253
+ event_type (TraceType): The type of event being traced.
254
+ start_time (float): The timestamp when the event started.
255
+ start_payload (Dict[str, Any]): The payload data from event start.
256
+ """
257
+ self.handlers = handlers
258
+ self.event_type = event_type
259
+ self.start_time = start_time
260
+ self.start_payload = start_payload
261
+ self.end_payload = {}
262
+ self.kwargs = {}
263
+
264
+ def on_end(self, payload: Dict[str, Any], **kwargs: Any) -> None:
265
+ """Set the end payload and additional kwargs for the event.
266
+
267
+ Args:
268
+ payload (Dict[str, Any]): The payload data for event end.
269
+ **kwargs (Any): Additional keyword arguments.
270
+ """
271
+ self.end_payload = payload
272
+ self.kwargs = kwargs
273
+
274
+ def on_log(self, message: str, **kwargs: Any) -> None:
275
+ """Log a message within this event context.
276
+
277
+ Args:
278
+ message (str): The log message.
279
+ **kwargs (Any): Additional keyword arguments.
280
+ """
281
+ kwargs["event_type"] = self.event_type
282
+ kwargs["start_time"] = self.start_time
283
+ kwargs["start_payload"] = self.start_payload
284
+ for handle in self.handlers:
285
+ handle.on_log(message, **kwargs)
286
+
287
+ def _end(self, start_payload: Dict[str, Any] = None) -> None:
288
+ """Finalize the event by calling on_end for all handlers.
289
+
290
+ Args:
291
+ start_payload (Dict[str, Any], optional): The payload data from
292
+ event start.
293
+ """
294
+ for handle in self.handlers:
295
+ handle.on_end(
296
+ self.event_type,
297
+ start_payload,
298
+ self.end_payload,
299
+ self.start_time,
300
+ **self.kwargs,
301
+ )
302
+
303
+ def set_trace_header(self, trace_header: Dict[str, Any]) -> None:
304
+ """Set trace header for compatible handlers.
305
+
306
+ Args:
307
+ trace_header (Dict[str, Any]): The trace header information.
308
+ """
309
+ # TODO: Implement this
@@ -0,0 +1,356 @@
1
+ # -*- coding: utf-8 -*-
2
+ import json
3
+ import logging
4
+ import os
5
+ import time
6
+ from datetime import datetime
7
+ from logging.handlers import RotatingFileHandler
8
+ from typing import Any, Dict, Optional
9
+
10
+ from pydantic import BaseModel
11
+
12
+ from .base import TracerHandler, TraceType
13
+
14
+ DEFAULT_LOG_NAME = "agentscope-runtime"
15
+
16
+ INFO_LOG_FILE_NAME = "info"
17
+ ERROR_LOG_FILE_NAME = "error"
18
+ LOG_EXTENSION = "log"
19
+ DS_SVC_ID = os.getenv("DS_SVC_ID", "test_id")
20
+ DS_SVC_NAME = os.getenv("DS_SVC_NAME", "test_name")
21
+
22
+
23
+ class LogContext(BaseModel):
24
+ """Pydantic model for log context data."""
25
+
26
+ time: str = ""
27
+ step: str = ""
28
+ model: str = ""
29
+ user_id: str = ""
30
+ task_id: str = ""
31
+ code: str = ""
32
+ message: str = ""
33
+ request_id: str = ""
34
+ context: Dict = {}
35
+ interval: Dict = {}
36
+ service_id: str = ""
37
+ service_name: str = ""
38
+ ds_service_id: str = ""
39
+ ds_service_name: str = ""
40
+
41
+ class Config:
42
+ extra = "ignore" # ignore additional key
43
+
44
+
45
+ class JsonFormatter(logging.Formatter):
46
+ """
47
+ Custom formatter to output logs in llm chat format.
48
+ """
49
+
50
+ def format(self, record: logging.LogRecord) -> str:
51
+ """Format a log record as JSON.
52
+
53
+ Args:
54
+ record (logging.LogRecord): The log record to format.
55
+
56
+ Returns:
57
+ str: The formatted log record as a JSON string.
58
+ """
59
+ log_record = {
60
+ "time": datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3],
61
+ "step": getattr(record, "step", None),
62
+ "model": getattr(record, "model", None),
63
+ "user_id": getattr(record, "user_id", None),
64
+ "code": getattr(record, "code", None),
65
+ "message": record.getMessage(),
66
+ "task_id": getattr(record, "task_id", None),
67
+ "request_id": getattr(record, "request_id", None),
68
+ "context": getattr(record, "context", None),
69
+ "interval": getattr(record, "interval", None),
70
+ "ds_service_id": DS_SVC_ID,
71
+ "ds_service_name": DS_SVC_NAME,
72
+ }
73
+ # Clean up any extra fields that are None (not provided)
74
+ log_record = {k: v for k, v in log_record.items() if v is not None}
75
+ if record.exc_info:
76
+ log_record["exc_info"] = self.formatException(record.exc_info)
77
+ return json.dumps(log_record, ensure_ascii=False)
78
+
79
+
80
+ class LocalLogHandler(TracerHandler):
81
+ """llm chat log handler for structured JSON logging."""
82
+
83
+ def __init__(
84
+ self,
85
+ log_level: int = logging.INFO,
86
+ log_file_name: Optional[str] = None,
87
+ log_dir: str = f"{os.getcwd()}/logs",
88
+ max_bytes: int = 1024 * 1024 * 1024,
89
+ backup_count: int = 7,
90
+ enable_console: bool = False,
91
+ propagate: bool = False,
92
+ **kwargs: Any,
93
+ ) -> None:
94
+ """Initialize the llm chat log handler.
95
+
96
+ Args:
97
+ log_level (int): The logging level. Defaults to logging.INFO.
98
+ log_file_name (Optional[str]): Prefix for log file names.
99
+ Defaults to None.
100
+ log_dir (str): Directory to save log files. Defaults to "./logs".
101
+ max_bytes (int): Maximum size in bytes for a single log file.
102
+ Defaults to 1GB.
103
+ backup_count (int): Number of log files to keep. Defaults to 7.
104
+ enable_console (bool): Whether to enable console logging.
105
+ Defaults to False.
106
+ propagate (bool): Whether to propagate log messages to parent
107
+ loggers.
108
+ Defaults to False.
109
+ **kwargs (Any): Additional keyword arguments.
110
+ """
111
+ self.logger = logging.getLogger(DEFAULT_LOG_NAME)
112
+ # Set propagate attribute to control log message propagation
113
+ # Note: Python logging propagate defaults to True, we set it to
114
+ # False by default
115
+ self.logger.propagate = propagate
116
+
117
+ if enable_console:
118
+ handler = logging.StreamHandler()
119
+ handler.setFormatter(JsonFormatter())
120
+ self.logger.addHandler(handler)
121
+ os.makedirs(log_dir, exist_ok=True)
122
+ self._set_file_handle(
123
+ log_dir=log_dir,
124
+ log_file_name=log_file_name,
125
+ max_bytes=max_bytes,
126
+ backup_count=backup_count,
127
+ )
128
+
129
+ self.logger.setLevel(log_level)
130
+ self.kwargs = kwargs
131
+
132
+ def _set_file_handle(
133
+ self,
134
+ log_dir: str,
135
+ log_file_name: Optional[str],
136
+ max_bytes: int,
137
+ backup_count: int,
138
+ ) -> None:
139
+ """Set up file handlers for logging.
140
+
141
+ Args:
142
+ log_dir (str): Directory to save the log files.
143
+ log_file_name (Optional[str]): Prefix name of log file name.
144
+ max_bytes (int): Maximum size in bytes for a single log file.
145
+ backup_count (int): The number of log files to keep.
146
+ """
147
+ log_file_name_prefix = f"{log_file_name}-" if log_file_name else ""
148
+
149
+ # Create file handlers with JsonFormatter
150
+ info_file_path = os.path.join(
151
+ log_dir,
152
+ f"{log_file_name_prefix}{INFO_LOG_FILE_NAME}.{LOG_EXTENSION}."
153
+ f"{os.getpid()}",
154
+ )
155
+ info_file_handler = RotatingFileHandler(
156
+ info_file_path,
157
+ mode="a",
158
+ maxBytes=max_bytes,
159
+ backupCount=backup_count,
160
+ )
161
+ info_file_handler.setFormatter(JsonFormatter())
162
+ info_file_handler.setLevel(logging.INFO)
163
+
164
+ # Create error file handler
165
+ error_file_path = os.path.join(
166
+ log_dir,
167
+ f"{log_file_name_prefix}{ERROR_LOG_FILE_NAME}.{LOG_EXTENSION}."
168
+ f"{os.getpid()}",
169
+ )
170
+ error_file_handler = RotatingFileHandler(
171
+ error_file_path,
172
+ mode="a",
173
+ maxBytes=max_bytes,
174
+ backupCount=backup_count,
175
+ )
176
+ error_file_handler.setFormatter(JsonFormatter())
177
+ error_file_handler.setLevel(logging.ERROR)
178
+
179
+ # Add handlers to the logger
180
+ self.logger.addHandler(info_file_handler)
181
+ self.logger.addHandler(error_file_handler)
182
+
183
+ @staticmethod
184
+ def _deep_update(original: Dict[str, Any], update: Dict[str, Any]) -> None:
185
+ """Recursively update a dictionary with another dictionary.
186
+
187
+ Args:
188
+ original (Dict[str, Any]): The original dictionary to update.
189
+ update (Dict[str, Any]): The dictionary containing updates.
190
+ """
191
+ for key, value in update.items():
192
+ if (
193
+ isinstance(value, dict)
194
+ and key in original
195
+ and isinstance(original[key], dict)
196
+ ):
197
+ LocalLogHandler._deep_update(original[key], value)
198
+ else:
199
+ original[key] = value
200
+
201
+ def on_start(
202
+ self,
203
+ event_type: TraceType,
204
+ payload: Dict[str, Any],
205
+ **kwargs: Any,
206
+ ) -> None:
207
+ """Handle the start of a trace event.
208
+
209
+ Args:
210
+ event_type (TraceType): The type of event being traced.
211
+ payload (Dict[str, Any]): The payload data for the event.
212
+ **kwargs (Any): Additional keyword arguments.
213
+ """
214
+ step = f"{event_type}_start"
215
+ request_id = payload.get("request_id", "")
216
+ context = payload.get("context", payload)
217
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
218
+ interval = {"type": step, "cost": 0}
219
+ runtime_context = LogContext(
220
+ time=timestamp,
221
+ step=step,
222
+ interval=interval,
223
+ context=context,
224
+ request_id=request_id,
225
+ )
226
+ try:
227
+ self.logger.info(
228
+ runtime_context.message,
229
+ extra=runtime_context.model_dump(exclude={"message"}),
230
+ )
231
+ except Exception as e:
232
+ import traceback
233
+
234
+ print(traceback.format_exc())
235
+ print(e)
236
+
237
+ def on_end(
238
+ self,
239
+ event_type: TraceType,
240
+ start_payload: Dict[str, Any],
241
+ end_payload: Dict[str, Any],
242
+ start_time: float,
243
+ **kwargs: Any,
244
+ ) -> None:
245
+ """Handle the end of a trace event.
246
+
247
+ Args:
248
+ event_type (TraceType): The type of event being traced.
249
+ start_payload (Dict[str, Any]): The payload data from event start.
250
+ end_payload (Dict[str, Any]): The payload data from event end.
251
+ start_time (float): The timestamp when the event started.
252
+ **kwargs (Any): Additional keyword arguments.
253
+ """
254
+
255
+ LocalLogHandler._deep_update(end_payload, start_payload)
256
+
257
+ step = f"{event_type}_end"
258
+ request_id = end_payload.get("request_id", "")
259
+ context = end_payload.get("context", end_payload)
260
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
261
+ duration = time.time() - start_time
262
+ interval = {"type": step, "cost": f"{duration:.3f}"}
263
+ runtime_context = LogContext(
264
+ time=timestamp,
265
+ step=step,
266
+ interval=interval,
267
+ context=context,
268
+ request_id=request_id,
269
+ )
270
+ self.logger.info(
271
+ runtime_context.message,
272
+ extra=runtime_context.model_dump(exclude={"message"}),
273
+ )
274
+
275
+ def on_log(self, message: str, **kwargs: Any) -> None:
276
+ """Handle a log message during tracing.
277
+
278
+ Args:
279
+ message (str): The log message.
280
+ **kwargs (Any): Additional keyword arguments including:
281
+ - step_suffix: Optional suffix for step identification
282
+ - event_type: The type of event being traced
283
+ - payload: The payload data
284
+ - start_time: The timestamp when the event started
285
+ - start_payload: The payload data from event start
286
+ """
287
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
288
+ if "step_suffix" in kwargs:
289
+ step_suffix = kwargs["step_suffix"]
290
+ event_type = kwargs["event_type"]
291
+ payload = kwargs["payload"]
292
+ start_time = kwargs["start_time"]
293
+ start_payload = kwargs["start_payload"]
294
+
295
+ LocalLogHandler._deep_update(payload, start_payload)
296
+
297
+ step = f"{event_type}_{step_suffix}"
298
+ duration = time.time() - start_time
299
+ interval = {"type": step, "cost": f"{duration:.3f}"}
300
+ else:
301
+ step = ""
302
+ interval = {"type": step, "cost": "0"}
303
+ payload = {}
304
+
305
+ runtime_context = LogContext(
306
+ time=timestamp,
307
+ step=step,
308
+ interval=interval,
309
+ context=payload,
310
+ message=message,
311
+ )
312
+ self.logger.info(
313
+ runtime_context.message,
314
+ extra=runtime_context.model_dump(exclude={"message"}),
315
+ )
316
+
317
+ def on_error(
318
+ self,
319
+ event_type: TraceType,
320
+ start_payload: Dict[str, Any],
321
+ error: Exception,
322
+ start_time: float,
323
+ traceback_info: str,
324
+ **kwargs: Any,
325
+ ) -> None:
326
+ """Handle an error during tracing.
327
+
328
+ Args:
329
+ event_type (TraceType): The type of event being traced.
330
+ start_payload (Dict[str, Any]): The payload data from event start.
331
+ error (Exception): The exception that occurred.
332
+ start_time (float): The timestamp when the event started.
333
+ traceback_info (str): The traceback information.
334
+ **kwargs (Any): Additional keyword arguments.
335
+ """
336
+ step = f"{event_type}_error"
337
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
338
+ duration = time.time() - start_time
339
+ interval = {"type": step, "cost": f"{duration:.3f}"}
340
+ if "context" not in start_payload:
341
+ start_payload["context"] = {}
342
+ start_payload["context"].update(
343
+ {"type": error.__class__.__name__, "details": traceback_info},
344
+ )
345
+ runtime_context = LogContext(
346
+ time=timestamp,
347
+ step=step,
348
+ interval=interval,
349
+ code=error.__class__.__name__,
350
+ message=str(error),
351
+ **start_payload,
352
+ )
353
+ self.logger.error(
354
+ runtime_context.message,
355
+ extra=runtime_context.model_dump(exclude={"message"}),
356
+ )