agentscope-runtime 0.1.5b2__py3-none-any.whl → 0.2.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.
- agentscope_runtime/common/__init__.py +0 -0
- agentscope_runtime/common/collections/in_memory_mapping.py +27 -0
- agentscope_runtime/common/collections/redis_mapping.py +42 -0
- agentscope_runtime/common/container_clients/__init__.py +0 -0
- agentscope_runtime/common/container_clients/agentrun_client.py +1098 -0
- agentscope_runtime/common/container_clients/docker_client.py +250 -0
- agentscope_runtime/{sandbox/manager → common}/container_clients/kubernetes_client.py +6 -13
- agentscope_runtime/engine/__init__.py +12 -0
- agentscope_runtime/engine/agents/agentscope_agent.py +567 -0
- agentscope_runtime/engine/agents/agno_agent.py +26 -27
- agentscope_runtime/engine/agents/autogen_agent.py +13 -8
- agentscope_runtime/engine/agents/langgraph_agent.py +52 -9
- agentscope_runtime/engine/agents/utils.py +53 -0
- agentscope_runtime/engine/app/__init__.py +6 -0
- agentscope_runtime/engine/app/agent_app.py +239 -0
- agentscope_runtime/engine/app/base_app.py +181 -0
- agentscope_runtime/engine/app/celery_mixin.py +92 -0
- agentscope_runtime/engine/deployers/adapter/responses/response_api_adapter_utils.py +5 -1
- agentscope_runtime/engine/deployers/base.py +1 -0
- agentscope_runtime/engine/deployers/cli_fc_deploy.py +39 -20
- agentscope_runtime/engine/deployers/kubernetes_deployer.py +12 -5
- agentscope_runtime/engine/deployers/local_deployer.py +61 -3
- agentscope_runtime/engine/deployers/modelstudio_deployer.py +201 -40
- agentscope_runtime/engine/deployers/utils/docker_image_utils/runner_image_factory.py +9 -0
- agentscope_runtime/engine/deployers/utils/package_project_utils.py +234 -3
- agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +567 -7
- agentscope_runtime/engine/deployers/utils/service_utils/standalone_main.py.j2 +211 -0
- agentscope_runtime/engine/deployers/utils/wheel_packager.py +1 -1
- agentscope_runtime/engine/helpers/helper.py +60 -41
- agentscope_runtime/engine/runner.py +40 -24
- agentscope_runtime/engine/schemas/agent_schemas.py +42 -0
- agentscope_runtime/engine/schemas/modelstudio_llm.py +14 -14
- agentscope_runtime/engine/services/sandbox_service.py +62 -70
- agentscope_runtime/engine/services/tablestore_memory_service.py +307 -0
- agentscope_runtime/engine/services/tablestore_rag_service.py +143 -0
- agentscope_runtime/engine/services/tablestore_session_history_service.py +293 -0
- agentscope_runtime/engine/services/utils/__init__.py +0 -0
- agentscope_runtime/engine/services/utils/tablestore_service_utils.py +352 -0
- agentscope_runtime/engine/tracing/__init__.py +9 -3
- agentscope_runtime/engine/tracing/asyncio_util.py +24 -0
- agentscope_runtime/engine/tracing/base.py +66 -34
- agentscope_runtime/engine/tracing/local_logging_handler.py +45 -31
- agentscope_runtime/engine/tracing/message_util.py +528 -0
- agentscope_runtime/engine/tracing/tracing_metric.py +20 -8
- agentscope_runtime/engine/tracing/tracing_util.py +130 -0
- agentscope_runtime/engine/tracing/wrapper.py +794 -169
- agentscope_runtime/sandbox/__init__.py +2 -0
- agentscope_runtime/sandbox/box/base/__init__.py +4 -0
- agentscope_runtime/sandbox/box/base/base_sandbox.py +6 -4
- agentscope_runtime/sandbox/box/browser/__init__.py +4 -0
- agentscope_runtime/sandbox/box/browser/browser_sandbox.py +10 -14
- agentscope_runtime/sandbox/box/dummy/__init__.py +4 -0
- agentscope_runtime/sandbox/box/dummy/dummy_sandbox.py +2 -1
- agentscope_runtime/sandbox/box/filesystem/__init__.py +4 -0
- agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +10 -7
- agentscope_runtime/sandbox/box/gui/__init__.py +4 -0
- agentscope_runtime/sandbox/box/gui/box/__init__.py +0 -0
- agentscope_runtime/sandbox/box/gui/gui_sandbox.py +81 -0
- agentscope_runtime/sandbox/box/sandbox.py +5 -2
- agentscope_runtime/sandbox/box/shared/routers/generic.py +20 -1
- agentscope_runtime/sandbox/box/training_box/__init__.py +4 -0
- agentscope_runtime/sandbox/box/training_box/training_box.py +7 -54
- agentscope_runtime/sandbox/build.py +143 -58
- agentscope_runtime/sandbox/client/http_client.py +87 -59
- agentscope_runtime/sandbox/client/training_client.py +0 -1
- agentscope_runtime/sandbox/constant.py +27 -1
- agentscope_runtime/sandbox/custom/custom_sandbox.py +7 -6
- agentscope_runtime/sandbox/custom/example.py +4 -3
- agentscope_runtime/sandbox/enums.py +1 -1
- agentscope_runtime/sandbox/manager/sandbox_manager.py +212 -106
- agentscope_runtime/sandbox/manager/server/app.py +82 -14
- agentscope_runtime/sandbox/manager/server/config.py +50 -3
- agentscope_runtime/sandbox/model/container.py +12 -23
- agentscope_runtime/sandbox/model/manager_config.py +93 -5
- agentscope_runtime/sandbox/registry.py +1 -1
- agentscope_runtime/sandbox/tools/gui/__init__.py +7 -0
- agentscope_runtime/sandbox/tools/gui/tool.py +77 -0
- agentscope_runtime/sandbox/tools/mcp_tool.py +6 -2
- agentscope_runtime/sandbox/tools/tool.py +4 -0
- agentscope_runtime/sandbox/utils.py +124 -0
- agentscope_runtime/version.py +1 -1
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/METADATA +246 -111
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/RECORD +96 -80
- agentscope_runtime/engine/agents/agentscope_agent/__init__.py +0 -6
- agentscope_runtime/engine/agents/agentscope_agent/agent.py +0 -401
- agentscope_runtime/engine/agents/agentscope_agent/hooks.py +0 -169
- agentscope_runtime/engine/agents/llm_agent.py +0 -51
- agentscope_runtime/engine/llms/__init__.py +0 -3
- agentscope_runtime/engine/llms/base_llm.py +0 -60
- agentscope_runtime/engine/llms/qwen_llm.py +0 -47
- agentscope_runtime/sandbox/manager/collections/in_memory_mapping.py +0 -22
- agentscope_runtime/sandbox/manager/collections/redis_mapping.py +0 -26
- agentscope_runtime/sandbox/manager/container_clients/__init__.py +0 -10
- agentscope_runtime/sandbox/manager/container_clients/docker_client.py +0 -422
- /agentscope_runtime/{sandbox/manager → common}/collections/__init__.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/base_mapping.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/base_queue.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/base_set.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/in_memory_queue.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/in_memory_set.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/redis_queue.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/redis_set.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/container_clients/base_client.py +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/WHEEL +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/entry_points.txt +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
#
|
|
2
|
+
# type: ignore
|
|
3
|
+
|
|
4
|
+
import contextvars
|
|
3
5
|
import inspect
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
9
|
+
import time
|
|
10
|
+
import uuid
|
|
11
|
+
from collections.abc import Callable
|
|
12
|
+
from copy import deepcopy
|
|
13
|
+
from enum import Enum
|
|
4
14
|
from functools import wraps
|
|
5
15
|
from typing import (
|
|
6
16
|
Any,
|
|
@@ -8,59 +18,101 @@ from typing import (
|
|
|
8
18
|
Dict,
|
|
9
19
|
Iterable,
|
|
10
20
|
Optional,
|
|
21
|
+
TypeVar,
|
|
11
22
|
Union,
|
|
12
23
|
)
|
|
13
|
-
from typing import AsyncIterable, AsyncIterator, Tuple, TypeVar
|
|
14
24
|
|
|
15
25
|
from pydantic import BaseModel
|
|
16
|
-
from
|
|
26
|
+
from opentelemetry.propagate import extract
|
|
27
|
+
from opentelemetry.context import attach
|
|
28
|
+
from opentelemetry.trace import StatusCode
|
|
29
|
+
from opentelemetry import trace as ot_trace
|
|
30
|
+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
|
|
31
|
+
OTLPSpanExporter as OTLPSpanGrpcExporter,
|
|
32
|
+
)
|
|
33
|
+
from opentelemetry.sdk.resources import SERVICE_NAME, SERVICE_VERSION, Resource
|
|
34
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
35
|
+
from opentelemetry.sdk.trace.export import (
|
|
36
|
+
BatchSpanProcessor,
|
|
37
|
+
ConsoleSpanExporter,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
from .asyncio_util import aenumerate
|
|
41
|
+
from .message_util import (
|
|
42
|
+
merge_incremental_chunk,
|
|
43
|
+
get_finish_reason,
|
|
44
|
+
)
|
|
17
45
|
|
|
18
|
-
from .base import Tracer,
|
|
46
|
+
from .base import Tracer, TracerHandler, EventContext
|
|
47
|
+
from .tracing_metric import TraceType
|
|
19
48
|
from .local_logging_handler import LocalLogHandler
|
|
49
|
+
from .tracing_util import TracingUtil
|
|
20
50
|
|
|
21
|
-
|
|
51
|
+
T_co = TypeVar("T_co", covariant=True)
|
|
22
52
|
|
|
23
53
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
start: int = 0,
|
|
27
|
-
) -> AsyncIterator[Tuple[int, T]]:
|
|
28
|
-
"""Asynchronously enumerate an async iterator from a given start value.
|
|
54
|
+
def _str_to_bool(value: str) -> bool:
|
|
55
|
+
"""Convert string to boolean value.
|
|
29
56
|
|
|
30
57
|
Args:
|
|
31
|
-
|
|
32
|
-
start (int): The starting value for enumeration. Defaults to 0.
|
|
58
|
+
value (str): String value to convert.
|
|
33
59
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
async iterable.
|
|
60
|
+
Returns:
|
|
61
|
+
bool: Boolean representation of the string.
|
|
37
62
|
"""
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
63
|
+
return value.lower() in ("true", "1", "yes", "on")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class MineType(str, Enum):
|
|
67
|
+
"""MIME type enumeration for content types."""
|
|
68
|
+
|
|
69
|
+
TEXT = "text/plain"
|
|
70
|
+
JSON = "application/json"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
_parent_span_context: contextvars.ContextVar = contextvars.ContextVar(
|
|
74
|
+
"_parent_span_context",
|
|
75
|
+
default=None,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
_current_request_id: contextvars.ContextVar[str] = contextvars.ContextVar(
|
|
79
|
+
"_current_request_id",
|
|
80
|
+
default="",
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
_current_trace_header: contextvars.ContextVar[dict] = contextvars.ContextVar(
|
|
84
|
+
"_current_trace_header",
|
|
85
|
+
default={},
|
|
86
|
+
)
|
|
42
87
|
|
|
43
88
|
|
|
44
|
-
def trace(
|
|
45
|
-
trace_type: Union[TraceType, str],
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
89
|
+
def trace( # pylint: disable=too-many-statements
|
|
90
|
+
trace_type: Union[TraceType, str, None] = None,
|
|
91
|
+
*,
|
|
92
|
+
trace_name: Optional[str] = None,
|
|
93
|
+
is_root_span: Optional[bool] = None,
|
|
94
|
+
get_finish_reason_func: Optional[
|
|
95
|
+
Callable[[Any], Optional[str]]
|
|
96
|
+
] = get_finish_reason,
|
|
97
|
+
merge_output_func: Optional[
|
|
98
|
+
Callable[[Any], Union[BaseModel, dict, str, None]]
|
|
99
|
+
] = merge_incremental_chunk,
|
|
49
100
|
) -> Any:
|
|
50
101
|
"""Decorator for tracing function execution.
|
|
51
102
|
|
|
52
103
|
Args:
|
|
53
104
|
trace_type (Union[TraceType, str]): The type of trace event.
|
|
54
|
-
|
|
55
|
-
|
|
105
|
+
trace_name (Optional[str]): The name of trace event.
|
|
106
|
+
is_root_span (Optional[bool]): Specify current span as root span
|
|
107
|
+
get_finish_reason_func(Optional[Callable]): The function to judge
|
|
108
|
+
if stopped
|
|
109
|
+
merge_output_func(Optional[Callable]): The function to merge outputs
|
|
56
110
|
|
|
57
111
|
Returns:
|
|
58
112
|
Any: The decorated function with tracing capabilities.
|
|
59
113
|
"""
|
|
60
|
-
if isinstance(trace_type, str):
|
|
61
|
-
trace_type = TraceType(trace_type)
|
|
62
114
|
|
|
63
|
-
def
|
|
115
|
+
def wrapper(func: Any) -> Any:
|
|
64
116
|
"""Wrapper function that applies tracing to the target function.
|
|
65
117
|
|
|
66
118
|
Args:
|
|
@@ -70,6 +122,7 @@ def trace(
|
|
|
70
122
|
Any: The wrapped function with appropriate tracing logic.
|
|
71
123
|
"""
|
|
72
124
|
|
|
125
|
+
@wraps(func)
|
|
73
126
|
async def async_exec(*args: Any, **kwargs: Any) -> Any:
|
|
74
127
|
"""Execute async function with tracing.
|
|
75
128
|
|
|
@@ -80,18 +133,99 @@ def trace(
|
|
|
80
133
|
Returns:
|
|
81
134
|
Any: The result of the traced function.
|
|
82
135
|
"""
|
|
83
|
-
|
|
84
|
-
|
|
136
|
+
|
|
137
|
+
_init_trace_context()
|
|
138
|
+
|
|
139
|
+
start_payload = _get_start_payload(args, kwargs, func)
|
|
140
|
+
|
|
141
|
+
trace_context = kwargs.get("trace_context") if kwargs else None
|
|
142
|
+
|
|
143
|
+
if trace_context:
|
|
144
|
+
attach(trace_context)
|
|
145
|
+
|
|
146
|
+
parent_ctx = (
|
|
147
|
+
trace_context
|
|
148
|
+
if trace_context
|
|
149
|
+
else _parent_span_context.get(None)
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
(
|
|
153
|
+
final_trace_type,
|
|
154
|
+
final_trace_name,
|
|
155
|
+
final_is_root_span,
|
|
156
|
+
) = _validate_trace_options(
|
|
85
157
|
trace_type,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
158
|
+
trace_name,
|
|
159
|
+
is_root_span,
|
|
160
|
+
func.__name__,
|
|
161
|
+
parent_ctx,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# Auto generate request_id for root span if needed
|
|
165
|
+
_set_request_id(parent_ctx)
|
|
166
|
+
|
|
167
|
+
common_attrs = TracingUtil.get_common_attributes() or {}
|
|
168
|
+
|
|
169
|
+
span_attributes = {
|
|
170
|
+
"gen_ai.span.kind": final_trace_type,
|
|
171
|
+
"gen_ai.user.query_root_flag": 1 if final_is_root_span else 0,
|
|
172
|
+
"input.mine_type": MineType.JSON,
|
|
173
|
+
"input.value": json.dumps(
|
|
174
|
+
start_payload,
|
|
175
|
+
ensure_ascii=False,
|
|
176
|
+
),
|
|
177
|
+
**common_attrs,
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
with _ot_tracer.start_as_current_span(
|
|
181
|
+
final_trace_name,
|
|
182
|
+
context=parent_ctx,
|
|
183
|
+
attributes=span_attributes,
|
|
184
|
+
) as span:
|
|
185
|
+
span.set_status(status=StatusCode.OK)
|
|
186
|
+
with _tracer.event(
|
|
187
|
+
span,
|
|
188
|
+
final_trace_name,
|
|
189
|
+
payload=start_payload,
|
|
190
|
+
) as event:
|
|
191
|
+
_parent_span_context.set(
|
|
192
|
+
ot_trace.set_span_in_context(span),
|
|
193
|
+
)
|
|
194
|
+
if _function_accepts_kwargs(func):
|
|
195
|
+
func_kwargs = kwargs.copy() if kwargs else {}
|
|
196
|
+
func_kwargs["trace_event"] = event
|
|
197
|
+
else:
|
|
198
|
+
func_kwargs = kwargs.copy() if kwargs else {}
|
|
199
|
+
|
|
200
|
+
try:
|
|
201
|
+
result = await func(*args, **func_kwargs)
|
|
202
|
+
end_payload = _obj_to_dict(result)
|
|
203
|
+
(
|
|
204
|
+
output_mine_type,
|
|
205
|
+
output_value,
|
|
206
|
+
) = _get_ot_type_and_value(end_payload)
|
|
207
|
+
span.set_attribute(
|
|
208
|
+
"output.mine_type",
|
|
209
|
+
output_mine_type,
|
|
210
|
+
)
|
|
211
|
+
span.set_attribute(
|
|
212
|
+
"output.value",
|
|
213
|
+
output_value,
|
|
214
|
+
)
|
|
215
|
+
event.on_end(payload=end_payload)
|
|
216
|
+
return result
|
|
217
|
+
except Exception as e:
|
|
218
|
+
span.set_status(
|
|
219
|
+
status=StatusCode.ERROR,
|
|
220
|
+
description=f"exception={e}",
|
|
221
|
+
)
|
|
222
|
+
event.on_log(str(e))
|
|
223
|
+
raise e
|
|
224
|
+
finally:
|
|
225
|
+
if not trace_context:
|
|
226
|
+
_parent_span_context.set(parent_ctx)
|
|
94
227
|
|
|
228
|
+
@wraps(func)
|
|
95
229
|
def sync_exec(*args: Any, **kwargs: Any) -> Any:
|
|
96
230
|
"""Execute sync function with tracing.
|
|
97
231
|
|
|
@@ -102,23 +236,103 @@ def trace(
|
|
|
102
236
|
Returns:
|
|
103
237
|
Any: The result of the traced function.
|
|
104
238
|
"""
|
|
105
|
-
|
|
106
|
-
|
|
239
|
+
|
|
240
|
+
_init_trace_context()
|
|
241
|
+
|
|
242
|
+
start_payload = _get_start_payload(args, kwargs, func)
|
|
243
|
+
|
|
244
|
+
trace_context = kwargs.get("trace_context") if kwargs else None
|
|
245
|
+
|
|
246
|
+
if trace_context:
|
|
247
|
+
attach(trace_context)
|
|
248
|
+
|
|
249
|
+
parent_ctx = (
|
|
250
|
+
trace_context
|
|
251
|
+
if trace_context
|
|
252
|
+
else _parent_span_context.get(None)
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
(
|
|
256
|
+
final_trace_type,
|
|
257
|
+
final_trace_name,
|
|
258
|
+
final_is_root_span,
|
|
259
|
+
) = _validate_trace_options(
|
|
107
260
|
trace_type,
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
261
|
+
trace_name,
|
|
262
|
+
is_root_span,
|
|
263
|
+
func.__name__,
|
|
264
|
+
parent_ctx,
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
# Auto generate request_id for root span if needed
|
|
268
|
+
_set_request_id(parent_ctx)
|
|
269
|
+
|
|
270
|
+
common_attrs = TracingUtil.get_common_attributes() or {}
|
|
271
|
+
|
|
272
|
+
span_attributes = {
|
|
273
|
+
"gen_ai.span.kind": final_trace_type,
|
|
274
|
+
"gen_ai.user.query_root_flag": 1 if final_is_root_span else 0,
|
|
275
|
+
"input.mine_type": MineType.JSON,
|
|
276
|
+
"input.value": json.dumps(
|
|
277
|
+
start_payload,
|
|
278
|
+
ensure_ascii=False,
|
|
279
|
+
),
|
|
280
|
+
**common_attrs,
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
with _ot_tracer.start_as_current_span(
|
|
284
|
+
final_trace_name,
|
|
285
|
+
context=parent_ctx,
|
|
286
|
+
attributes=span_attributes,
|
|
287
|
+
) as span:
|
|
288
|
+
span.set_status(status=StatusCode.OK)
|
|
289
|
+
with _tracer.event(
|
|
290
|
+
span,
|
|
291
|
+
final_trace_name,
|
|
292
|
+
payload=start_payload,
|
|
293
|
+
) as event:
|
|
294
|
+
_parent_span_context.set(
|
|
295
|
+
ot_trace.set_span_in_context(span),
|
|
296
|
+
)
|
|
297
|
+
if _function_accepts_kwargs(func):
|
|
298
|
+
func_kwargs = kwargs.copy() if kwargs else {}
|
|
299
|
+
func_kwargs["trace_event"] = event
|
|
300
|
+
else:
|
|
301
|
+
func_kwargs = kwargs.copy() if kwargs else {}
|
|
302
|
+
|
|
303
|
+
try:
|
|
304
|
+
result = func(*args, **func_kwargs)
|
|
305
|
+
end_payload = _obj_to_dict(result)
|
|
306
|
+
(
|
|
307
|
+
output_mine_type,
|
|
308
|
+
output_value,
|
|
309
|
+
) = _get_ot_type_and_value(end_payload)
|
|
310
|
+
span.set_attribute(
|
|
311
|
+
"output.mine_type",
|
|
312
|
+
output_mine_type,
|
|
313
|
+
)
|
|
314
|
+
span.set_attribute(
|
|
315
|
+
"output.value",
|
|
316
|
+
output_value,
|
|
317
|
+
)
|
|
318
|
+
event.on_end(payload=end_payload)
|
|
319
|
+
return result
|
|
320
|
+
except Exception as e:
|
|
321
|
+
span.set_status(
|
|
322
|
+
status=StatusCode.ERROR,
|
|
323
|
+
description=f"exception={e}",
|
|
324
|
+
)
|
|
325
|
+
event.on_log(str(e))
|
|
326
|
+
raise e
|
|
327
|
+
finally:
|
|
328
|
+
if not trace_context:
|
|
329
|
+
_parent_span_context.set(parent_ctx)
|
|
116
330
|
|
|
117
331
|
@wraps(func)
|
|
118
|
-
async def async_iter_task(
|
|
332
|
+
async def async_iter_task( # pylint: disable=too-many-statements
|
|
119
333
|
*args: Any,
|
|
120
334
|
**kwargs: Any,
|
|
121
|
-
) -> AsyncGenerator[
|
|
335
|
+
) -> AsyncGenerator[T_co, None]:
|
|
122
336
|
"""Execute async generator function with tracing.
|
|
123
337
|
|
|
124
338
|
Args:
|
|
@@ -126,76 +340,132 @@ def trace(
|
|
|
126
340
|
**kwargs (Any): Keyword arguments for the function.
|
|
127
341
|
|
|
128
342
|
Yields:
|
|
129
|
-
|
|
343
|
+
T_co: Items from the original generator with tracing.
|
|
130
344
|
"""
|
|
131
|
-
|
|
132
|
-
|
|
345
|
+
_init_trace_context()
|
|
346
|
+
|
|
347
|
+
start_payload = _get_start_payload(args, kwargs, func)
|
|
348
|
+
|
|
349
|
+
trace_context = kwargs.get("trace_context") if kwargs else None
|
|
350
|
+
|
|
351
|
+
if trace_context:
|
|
352
|
+
attach(trace_context)
|
|
353
|
+
|
|
354
|
+
parent_ctx = (
|
|
355
|
+
trace_context
|
|
356
|
+
if trace_context
|
|
357
|
+
else _parent_span_context.get(None)
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
(
|
|
361
|
+
final_trace_type,
|
|
362
|
+
final_trace_name,
|
|
363
|
+
final_is_root_span,
|
|
364
|
+
) = _validate_trace_options(
|
|
133
365
|
trace_type,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
366
|
+
trace_name,
|
|
367
|
+
is_root_span,
|
|
368
|
+
func.__name__,
|
|
369
|
+
parent_ctx,
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
# Auto generate request_id for root span if needed
|
|
373
|
+
_set_request_id(parent_ctx)
|
|
374
|
+
|
|
375
|
+
common_attrs = TracingUtil.get_common_attributes() or {}
|
|
376
|
+
|
|
377
|
+
span_attributes = {
|
|
378
|
+
"gen_ai.span.kind": final_trace_type,
|
|
379
|
+
"gen_ai.user.query_root_flag": 1 if final_is_root_span else 0,
|
|
380
|
+
"input.mine_type": MineType.JSON,
|
|
381
|
+
"input.value": json.dumps(
|
|
382
|
+
start_payload,
|
|
383
|
+
ensure_ascii=False,
|
|
384
|
+
),
|
|
385
|
+
**common_attrs,
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
with _ot_tracer.start_as_current_span(
|
|
389
|
+
final_trace_name,
|
|
390
|
+
context=parent_ctx,
|
|
391
|
+
attributes=span_attributes,
|
|
392
|
+
) as span:
|
|
393
|
+
span.set_status(status=StatusCode.OK)
|
|
394
|
+
with _tracer.event(
|
|
395
|
+
span,
|
|
396
|
+
final_trace_name,
|
|
397
|
+
payload=start_payload,
|
|
398
|
+
) as event:
|
|
399
|
+
_parent_span_context.set(
|
|
400
|
+
ot_trace.set_span_in_context(span),
|
|
401
|
+
)
|
|
402
|
+
if _function_accepts_kwargs(func):
|
|
403
|
+
func_kwargs = kwargs.copy() if kwargs else {}
|
|
404
|
+
func_kwargs["trace_event"] = event
|
|
405
|
+
else:
|
|
406
|
+
func_kwargs = kwargs.copy() if kwargs else {}
|
|
407
|
+
|
|
408
|
+
cumulated = []
|
|
409
|
+
|
|
410
|
+
async def iter_entry() -> AsyncGenerator[T_co, None]:
|
|
411
|
+
"""Internal async generator for processing items.
|
|
412
|
+
|
|
413
|
+
Yields:
|
|
414
|
+
T_co: Items from the original generator with
|
|
415
|
+
tracing.
|
|
416
|
+
"""
|
|
417
|
+
try:
|
|
418
|
+
start_time = int(time.time() * 1000)
|
|
419
|
+
async for i, resp in aenumerate(
|
|
420
|
+
func(*args, **func_kwargs),
|
|
421
|
+
): # type: ignore
|
|
422
|
+
yield resp
|
|
423
|
+
cumulated.append(resp)
|
|
424
|
+
|
|
425
|
+
if i == 0:
|
|
426
|
+
_trace_first_resp(
|
|
427
|
+
resp,
|
|
428
|
+
event,
|
|
429
|
+
span,
|
|
430
|
+
start_time,
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
if get_finish_reason_func is not None:
|
|
434
|
+
_trace_last_resp(
|
|
435
|
+
resp,
|
|
436
|
+
get_finish_reason_func,
|
|
437
|
+
event,
|
|
438
|
+
span,
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
if cumulated and merge_output_func is not None:
|
|
442
|
+
_trace_merged_resp(
|
|
443
|
+
cumulated,
|
|
444
|
+
merge_output_func,
|
|
445
|
+
event,
|
|
446
|
+
span,
|
|
158
447
|
)
|
|
159
|
-
# todo: support more components
|
|
160
|
-
if isinstance(resp, ChatCompletionChunk):
|
|
161
|
-
if len(resp.choices) > 0:
|
|
162
|
-
cumulated.append(resp)
|
|
163
|
-
if (
|
|
164
|
-
resp.choices[0].finish_reason
|
|
165
|
-
is not None
|
|
166
|
-
):
|
|
167
|
-
if (
|
|
168
|
-
resp.choices[0].finish_reason
|
|
169
|
-
== "stop"
|
|
170
|
-
):
|
|
171
|
-
step_suffix = "last_resp"
|
|
172
|
-
else:
|
|
173
|
-
step_suffix = resp.choices[
|
|
174
|
-
0
|
|
175
|
-
].finish_reason
|
|
176
|
-
event.on_log(
|
|
177
|
-
"",
|
|
178
|
-
**{
|
|
179
|
-
"step_suffix": step_suffix,
|
|
180
|
-
"payload": resp.model_dump(),
|
|
181
|
-
},
|
|
182
|
-
)
|
|
183
|
-
elif resp.usage:
|
|
184
|
-
cumulated.append(resp)
|
|
185
448
|
|
|
449
|
+
except Exception as e:
|
|
450
|
+
span.set_status(
|
|
451
|
+
status=StatusCode.ERROR,
|
|
452
|
+
description=f"exception={e}",
|
|
453
|
+
)
|
|
454
|
+
event.on_log(str(e))
|
|
455
|
+
raise e
|
|
456
|
+
finally:
|
|
457
|
+
if not trace_context:
|
|
458
|
+
_parent_span_context.set(parent_ctx)
|
|
459
|
+
|
|
460
|
+
try:
|
|
461
|
+
async for resp in iter_entry():
|
|
186
462
|
yield resp
|
|
463
|
+
|
|
187
464
|
except Exception as e:
|
|
188
465
|
raise e
|
|
189
466
|
|
|
190
|
-
try:
|
|
191
|
-
async for resp in iter_entry():
|
|
192
|
-
yield resp
|
|
193
|
-
|
|
194
|
-
except Exception as e:
|
|
195
|
-
raise e
|
|
196
|
-
|
|
197
467
|
@wraps(func)
|
|
198
|
-
def iter_task(*args: Any, **kwargs: Any) -> Iterable[
|
|
468
|
+
def iter_task(*args: Any, **kwargs: Any) -> Iterable[T_co]:
|
|
199
469
|
"""Execute generator function with tracing.
|
|
200
470
|
|
|
201
471
|
Args:
|
|
@@ -203,84 +473,175 @@ def trace(
|
|
|
203
473
|
**kwargs (Any): Keyword arguments for the function.
|
|
204
474
|
|
|
205
475
|
Yields:
|
|
206
|
-
|
|
476
|
+
T_co: Items from the traced generator.
|
|
207
477
|
"""
|
|
208
|
-
|
|
209
|
-
|
|
478
|
+
_init_trace_context()
|
|
479
|
+
|
|
480
|
+
start_payload = _get_start_payload(args, kwargs, func)
|
|
481
|
+
|
|
482
|
+
trace_context = kwargs.get("trace_context") if kwargs else None
|
|
483
|
+
|
|
484
|
+
if trace_context:
|
|
485
|
+
attach(trace_context)
|
|
486
|
+
|
|
487
|
+
parent_ctx = (
|
|
488
|
+
trace_context
|
|
489
|
+
if trace_context
|
|
490
|
+
else _parent_span_context.get(None)
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
(
|
|
494
|
+
final_trace_type,
|
|
495
|
+
final_trace_name,
|
|
496
|
+
final_is_root_span,
|
|
497
|
+
) = _validate_trace_options(
|
|
210
498
|
trace_type,
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
499
|
+
trace_name,
|
|
500
|
+
is_root_span,
|
|
501
|
+
func.__name__,
|
|
502
|
+
parent_ctx,
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
# Auto generate request_id for root span if needed
|
|
506
|
+
_set_request_id(parent_ctx)
|
|
507
|
+
|
|
508
|
+
common_attrs = TracingUtil.get_common_attributes() or {}
|
|
509
|
+
|
|
510
|
+
span_attributes = {
|
|
511
|
+
"gen_ai.span.kind": final_trace_type,
|
|
512
|
+
"gen_ai.user.query_root_flag": 1 if final_is_root_span else 0,
|
|
513
|
+
"input.mine_type": MineType.JSON,
|
|
514
|
+
"input.value": json.dumps(
|
|
515
|
+
start_payload,
|
|
516
|
+
ensure_ascii=False,
|
|
517
|
+
),
|
|
518
|
+
**common_attrs,
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
with _ot_tracer.start_as_current_span(
|
|
522
|
+
final_trace_name,
|
|
523
|
+
context=parent_ctx,
|
|
524
|
+
attributes=span_attributes,
|
|
525
|
+
) as span:
|
|
526
|
+
span.set_status(status=StatusCode.OK)
|
|
527
|
+
with _tracer.event(
|
|
528
|
+
span,
|
|
529
|
+
final_trace_name,
|
|
530
|
+
payload=start_payload,
|
|
531
|
+
) as event:
|
|
532
|
+
_parent_span_context.set(
|
|
533
|
+
ot_trace.set_span_in_context(span),
|
|
534
|
+
)
|
|
535
|
+
try:
|
|
536
|
+
if _function_accepts_kwargs(func):
|
|
537
|
+
func_kwargs = kwargs.copy() if kwargs else {}
|
|
538
|
+
func_kwargs["trace_event"] = event
|
|
539
|
+
else:
|
|
540
|
+
func_kwargs = kwargs.copy() if kwargs else {}
|
|
541
|
+
|
|
542
|
+
cumulated = []
|
|
543
|
+
start_time = int(time.time() * 1000)
|
|
544
|
+
for i, resp in enumerate(func(*args, **func_kwargs)):
|
|
545
|
+
yield resp
|
|
229
546
|
cumulated.append(resp)
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
**{
|
|
238
|
-
"step_suffix": step_suffix,
|
|
239
|
-
"payload": resp.model_dump(),
|
|
240
|
-
},
|
|
547
|
+
|
|
548
|
+
if i == 0:
|
|
549
|
+
_trace_first_resp(
|
|
550
|
+
resp,
|
|
551
|
+
event,
|
|
552
|
+
span,
|
|
553
|
+
start_time,
|
|
241
554
|
)
|
|
242
|
-
elif resp.usage:
|
|
243
|
-
cumulated.append(resp)
|
|
244
555
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
556
|
+
if get_finish_reason_func is not None:
|
|
557
|
+
_trace_last_resp(
|
|
558
|
+
resp,
|
|
559
|
+
get_finish_reason_func,
|
|
560
|
+
event,
|
|
561
|
+
span,
|
|
562
|
+
)
|
|
248
563
|
|
|
564
|
+
if cumulated and merge_output_func is not None:
|
|
565
|
+
_trace_merged_resp(
|
|
566
|
+
cumulated,
|
|
567
|
+
merge_output_func,
|
|
568
|
+
event,
|
|
569
|
+
span,
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
except Exception as e:
|
|
573
|
+
span.set_status(
|
|
574
|
+
status=StatusCode.ERROR,
|
|
575
|
+
description=f"exception={e}",
|
|
576
|
+
)
|
|
577
|
+
event.on_log(str(e))
|
|
578
|
+
raise e
|
|
579
|
+
finally:
|
|
580
|
+
if not trace_context:
|
|
581
|
+
_parent_span_context.set(parent_ctx)
|
|
582
|
+
|
|
583
|
+
# Choose the appropriate wrapper based on function type
|
|
249
584
|
if inspect.isasyncgenfunction(func):
|
|
250
|
-
|
|
585
|
+
wrapper_func = async_iter_task
|
|
251
586
|
elif inspect.isgeneratorfunction(func):
|
|
252
|
-
|
|
587
|
+
wrapper_func = iter_task
|
|
253
588
|
elif inspect.iscoroutinefunction(func):
|
|
254
|
-
|
|
589
|
+
wrapper_func = async_exec
|
|
255
590
|
else:
|
|
256
|
-
|
|
591
|
+
wrapper_func = sync_exec
|
|
592
|
+
|
|
593
|
+
# Preserve the original function's signature
|
|
594
|
+
try:
|
|
595
|
+
wrapper_func.__signature__ = inspect.signature(func)
|
|
596
|
+
except (ValueError, TypeError):
|
|
597
|
+
pass
|
|
598
|
+
|
|
599
|
+
return wrapper_func
|
|
257
600
|
|
|
258
|
-
return
|
|
601
|
+
return wrapper
|
|
259
602
|
|
|
260
603
|
|
|
261
|
-
def _get_start_payload(args: Any, kwargs: Any) -> Dict:
|
|
604
|
+
def _get_start_payload(args: Any, kwargs: Any, func: Any = None) -> Dict:
|
|
262
605
|
"""Extract and format the start payload from function arguments.
|
|
263
606
|
|
|
264
607
|
Args:
|
|
265
608
|
args (Any): Positional arguments from the function call.
|
|
266
609
|
kwargs (Any): Keyword arguments from the function call.
|
|
610
|
+
func (Any): The function being traced (optional).
|
|
267
611
|
|
|
268
612
|
Returns:
|
|
269
613
|
Dict: The formatted start payload for tracing.
|
|
270
614
|
"""
|
|
271
|
-
dict_args = {}
|
|
272
|
-
if isinstance(args, tuple) and len(args) > 1:
|
|
273
|
-
dict_args = _obj_to_dict(args[1:])
|
|
274
|
-
|
|
275
|
-
dict_kwargs = _obj_to_dict(kwargs)
|
|
276
|
-
dict_kwargs = {
|
|
277
|
-
key: value
|
|
278
|
-
for key, value in dict_kwargs.items()
|
|
279
|
-
if not key.startswith("trace_")
|
|
280
|
-
}
|
|
281
|
-
|
|
282
615
|
merged = {}
|
|
283
|
-
|
|
616
|
+
|
|
617
|
+
# 处理位置参数:尝试将位置参数与函数签名中的参数名对应
|
|
618
|
+
if func is not None and isinstance(args, tuple) and len(args) > 0:
|
|
619
|
+
try:
|
|
620
|
+
sig = inspect.signature(func)
|
|
621
|
+
params = list(sig.parameters.values())
|
|
622
|
+
|
|
623
|
+
# 跳过self参数(如果是实例方法)
|
|
624
|
+
start_idx = 0
|
|
625
|
+
if params and params[0].name == "self":
|
|
626
|
+
start_idx = 1
|
|
627
|
+
|
|
628
|
+
# 将位置参数与参数名对应
|
|
629
|
+
for i, arg in enumerate(args[start_idx:], start=start_idx):
|
|
630
|
+
if i < len(params):
|
|
631
|
+
param = params[i]
|
|
632
|
+
# 只处理位置参数和位置或关键字参数,跳过*args和**kwargs
|
|
633
|
+
if param.kind in (
|
|
634
|
+
inspect.Parameter.POSITIONAL_ONLY,
|
|
635
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
636
|
+
):
|
|
637
|
+
merged[param.name] = _obj_to_dict(arg)
|
|
638
|
+
except (ValueError, TypeError, IndexError):
|
|
639
|
+
# 如果无法获取函数签名,回退到原来的逻辑
|
|
640
|
+
pass
|
|
641
|
+
|
|
642
|
+
# 如果没有函数信息或无法解析,使用原来的逻辑
|
|
643
|
+
if not merged and isinstance(args, tuple) and len(args) > 0:
|
|
644
|
+
dict_args = _obj_to_dict(args)
|
|
284
645
|
if isinstance(dict_args, list):
|
|
285
646
|
for item in dict_args:
|
|
286
647
|
if isinstance(item, dict):
|
|
@@ -288,12 +649,185 @@ def _get_start_payload(args: Any, kwargs: Any) -> Dict:
|
|
|
288
649
|
elif isinstance(dict_args, dict):
|
|
289
650
|
merged.update(dict_args)
|
|
290
651
|
|
|
652
|
+
# 处理关键字参数
|
|
653
|
+
dict_kwargs = _obj_to_dict(kwargs)
|
|
654
|
+
dict_kwargs = {
|
|
655
|
+
key: value
|
|
656
|
+
for key, value in dict_kwargs.items()
|
|
657
|
+
if not key.startswith("trace_")
|
|
658
|
+
}
|
|
659
|
+
|
|
291
660
|
if dict_kwargs:
|
|
292
661
|
merged.update(dict_kwargs)
|
|
293
662
|
|
|
294
663
|
return merged
|
|
295
664
|
|
|
296
665
|
|
|
666
|
+
def _init_trace_context() -> None:
|
|
667
|
+
current_req_id = _current_request_id.get("")
|
|
668
|
+
user_req_id = TracingUtil.get_request_id()
|
|
669
|
+
if user_req_id and user_req_id != current_req_id:
|
|
670
|
+
_parent_span_context.set(None)
|
|
671
|
+
_current_request_id.set(user_req_id)
|
|
672
|
+
|
|
673
|
+
current_trace_header = _current_trace_header.get({})
|
|
674
|
+
user_trace_header = TracingUtil.get_trace_header()
|
|
675
|
+
if user_trace_header and user_trace_header != current_trace_header:
|
|
676
|
+
_current_trace_header.set(user_trace_header)
|
|
677
|
+
context = extract(user_trace_header)
|
|
678
|
+
attach(context)
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
def _set_request_id(parent_ctx: Any) -> None:
|
|
682
|
+
"""Auto generate request_id for root span if not already set.
|
|
683
|
+
|
|
684
|
+
Args:
|
|
685
|
+
parent_ctx: Parent context. If None, this is a root span.
|
|
686
|
+
"""
|
|
687
|
+
# Check if we already have a request_id (from context var or baggage)
|
|
688
|
+
current_request_id = TracingUtil.get_request_id()
|
|
689
|
+
|
|
690
|
+
if parent_ctx is None:
|
|
691
|
+
# This is a root span
|
|
692
|
+
if not current_request_id:
|
|
693
|
+
# For root spans without request_id, generate a new one
|
|
694
|
+
current_parent_span = _parent_span_context.get(None)
|
|
695
|
+
if current_parent_span is None:
|
|
696
|
+
# This is a truly new request, generate a new request_id
|
|
697
|
+
new_request_id = str(uuid.uuid4())
|
|
698
|
+
TracingUtil.set_request_id(new_request_id)
|
|
699
|
+
else:
|
|
700
|
+
if current_request_id and not TracingUtil.get_common_attributes().get(
|
|
701
|
+
"request_id",
|
|
702
|
+
):
|
|
703
|
+
# Set common attributes from baggage request_id
|
|
704
|
+
TracingUtil.set_request_id(current_request_id)
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
def _trace_first_resp(
|
|
708
|
+
resp: Any,
|
|
709
|
+
event: EventContext,
|
|
710
|
+
span: Any,
|
|
711
|
+
start_time: int,
|
|
712
|
+
) -> None:
|
|
713
|
+
payload = _obj_to_dict(resp)
|
|
714
|
+
event.on_log(
|
|
715
|
+
"",
|
|
716
|
+
**{
|
|
717
|
+
"step_suffix": "first_resp",
|
|
718
|
+
"payload": payload,
|
|
719
|
+
},
|
|
720
|
+
)
|
|
721
|
+
span.set_attribute(
|
|
722
|
+
"gen_ai.response.first_delay",
|
|
723
|
+
int(time.time() * 1000) - start_time,
|
|
724
|
+
)
|
|
725
|
+
_, output_value = _get_ot_type_and_value(payload)
|
|
726
|
+
span.set_attribute(
|
|
727
|
+
"gen_ai.response.first_pkg",
|
|
728
|
+
output_value,
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
def _trace_last_resp(
|
|
733
|
+
resp: Any,
|
|
734
|
+
func: Callable,
|
|
735
|
+
event: EventContext,
|
|
736
|
+
span: Any,
|
|
737
|
+
) -> None:
|
|
738
|
+
resp_copy = deepcopy(resp)
|
|
739
|
+
|
|
740
|
+
finish_reason = func(resp_copy)
|
|
741
|
+
if finish_reason:
|
|
742
|
+
step_suffix = "last_resp" if finish_reason == "stop" else finish_reason
|
|
743
|
+
payload = _obj_to_dict(resp_copy)
|
|
744
|
+
event.on_log(
|
|
745
|
+
"",
|
|
746
|
+
**{
|
|
747
|
+
"step_suffix": step_suffix,
|
|
748
|
+
"payload": payload,
|
|
749
|
+
},
|
|
750
|
+
)
|
|
751
|
+
_, output_value = _get_ot_type_and_value(payload)
|
|
752
|
+
span.set_attribute(
|
|
753
|
+
"gen_ai.response.pkg_" + finish_reason,
|
|
754
|
+
output_value,
|
|
755
|
+
)
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
def _trace_merged_resp(
|
|
759
|
+
cumulated: Any,
|
|
760
|
+
func: Callable,
|
|
761
|
+
event: EventContext,
|
|
762
|
+
span: Any,
|
|
763
|
+
) -> None:
|
|
764
|
+
cumulated_copy = deepcopy(cumulated)
|
|
765
|
+
|
|
766
|
+
merged_output = func(cumulated_copy)
|
|
767
|
+
end_payload = _obj_to_dict(merged_output)
|
|
768
|
+
output_mine_type, output_value = _get_ot_type_and_value(end_payload)
|
|
769
|
+
span.set_attribute(
|
|
770
|
+
"output.mine_type",
|
|
771
|
+
output_mine_type,
|
|
772
|
+
)
|
|
773
|
+
span.set_attribute(
|
|
774
|
+
"output.value",
|
|
775
|
+
output_value,
|
|
776
|
+
)
|
|
777
|
+
event.on_end(
|
|
778
|
+
payload=end_payload,
|
|
779
|
+
)
|
|
780
|
+
|
|
781
|
+
|
|
782
|
+
def _get_ot_type_and_value(payload: Any) -> tuple[MineType, Any]:
|
|
783
|
+
if isinstance(payload, dict):
|
|
784
|
+
mine_type = MineType.JSON
|
|
785
|
+
value = json.dumps(payload, ensure_ascii=False)
|
|
786
|
+
else:
|
|
787
|
+
mine_type = MineType.TEXT
|
|
788
|
+
if isinstance(payload, (str, int, float, bool)):
|
|
789
|
+
value = payload
|
|
790
|
+
else:
|
|
791
|
+
value = str(payload)
|
|
792
|
+
return mine_type, value
|
|
793
|
+
|
|
794
|
+
|
|
795
|
+
def _validate_trace_options(
|
|
796
|
+
trace_type: Union[TraceType, str, None] = None,
|
|
797
|
+
trace_name: Optional[str] = None,
|
|
798
|
+
is_root_span: Optional[bool] = None,
|
|
799
|
+
function_name: Optional[str] = None,
|
|
800
|
+
parent_ctx: Optional[Any] = None,
|
|
801
|
+
) -> tuple[str, str | None, bool | None]:
|
|
802
|
+
out_is_root_span = (
|
|
803
|
+
is_root_span
|
|
804
|
+
and parent_ctx is None
|
|
805
|
+
and not _parent_span_context.get(None)
|
|
806
|
+
)
|
|
807
|
+
|
|
808
|
+
if out_is_root_span:
|
|
809
|
+
out_trace_type = TraceType.CHAIN
|
|
810
|
+
out_trace_name = "FullCodeApp"
|
|
811
|
+
else:
|
|
812
|
+
if trace_type:
|
|
813
|
+
if isinstance(trace_type, str):
|
|
814
|
+
out_trace_type = TraceType(trace_type)
|
|
815
|
+
else:
|
|
816
|
+
out_trace_type = trace_type
|
|
817
|
+
else:
|
|
818
|
+
out_trace_type = TraceType.OTHER
|
|
819
|
+
|
|
820
|
+
if trace_name:
|
|
821
|
+
out_trace_name = trace_name
|
|
822
|
+
else:
|
|
823
|
+
if function_name:
|
|
824
|
+
out_trace_name = function_name
|
|
825
|
+
else:
|
|
826
|
+
out_trace_name = str(out_trace_type).lower()
|
|
827
|
+
|
|
828
|
+
return out_trace_type, out_trace_name, out_is_root_span
|
|
829
|
+
|
|
830
|
+
|
|
297
831
|
def _obj_to_dict(obj: Any) -> Any:
|
|
298
832
|
"""Convert an object to a dictionary representation for tracing.
|
|
299
833
|
|
|
@@ -304,10 +838,12 @@ def _obj_to_dict(obj: Any) -> Any:
|
|
|
304
838
|
Any: The dictionary representation of the object, or the object
|
|
305
839
|
itself if it's a primitive type.
|
|
306
840
|
"""
|
|
307
|
-
if
|
|
841
|
+
if obj is None:
|
|
842
|
+
return {}
|
|
843
|
+
elif isinstance(obj, (str, int, float, bool, type(None))):
|
|
308
844
|
return obj
|
|
309
845
|
elif isinstance(obj, dict):
|
|
310
|
-
return {k: _obj_to_dict(v) for k, v in obj.items()}
|
|
846
|
+
return {k: _obj_to_dict(v) for k, v in obj.items()}
|
|
311
847
|
elif isinstance(obj, (list, set, tuple)):
|
|
312
848
|
return [_obj_to_dict(item) for item in obj]
|
|
313
849
|
elif isinstance(obj, BaseModel):
|
|
@@ -319,3 +855,92 @@ def _obj_to_dict(obj: Any) -> Any:
|
|
|
319
855
|
except Exception as e:
|
|
320
856
|
print(f"{obj} str method failed with error: {e}")
|
|
321
857
|
return result
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
def _function_accepts_kwargs(func: Any) -> bool:
|
|
861
|
+
"""Check if a function accepts **kwargs parameter.
|
|
862
|
+
|
|
863
|
+
Args:
|
|
864
|
+
func (Any): The function to check.
|
|
865
|
+
|
|
866
|
+
Returns:
|
|
867
|
+
bool: True if the function accepts **kwargs, False otherwise.
|
|
868
|
+
"""
|
|
869
|
+
try:
|
|
870
|
+
sig = inspect.signature(func)
|
|
871
|
+
return any(
|
|
872
|
+
param.kind == inspect.Parameter.VAR_KEYWORD
|
|
873
|
+
for param in sig.parameters.values()
|
|
874
|
+
)
|
|
875
|
+
except (ValueError, TypeError):
|
|
876
|
+
return False
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
def _get_service_name() -> str:
|
|
880
|
+
"""Get service name
|
|
881
|
+
Returns:
|
|
882
|
+
str: The extracted service name, or the original name if extraction
|
|
883
|
+
fails.
|
|
884
|
+
"""
|
|
885
|
+
|
|
886
|
+
service_name = os.getenv("SERVICE_NAME") or os.getenv("DS_SVC_NAME")
|
|
887
|
+
if not service_name:
|
|
888
|
+
service_name = "agentscope_runtime-service"
|
|
889
|
+
|
|
890
|
+
pattern = r"deployment\.([^-]+(?:-[^-]+)*?)(?=-[^-]+-[^-]+$)"
|
|
891
|
+
match = re.search(pattern, service_name)
|
|
892
|
+
if match:
|
|
893
|
+
return match.group(1)
|
|
894
|
+
else:
|
|
895
|
+
return service_name
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
def _get_tracer() -> Tracer:
|
|
899
|
+
handlers: list[TracerHandler] = []
|
|
900
|
+
if _str_to_bool(os.getenv("TRACE_ENABLE_LOG", "true")):
|
|
901
|
+
handlers.append(LocalLogHandler(enable_console=True))
|
|
902
|
+
|
|
903
|
+
tracer = Tracer(handlers=handlers)
|
|
904
|
+
return tracer
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
# TODO: support more tracing protocols and platforms
|
|
908
|
+
def _get_ot_tracer() -> ot_trace.Tracer:
|
|
909
|
+
"""Get the OpenTelemetry tracer.
|
|
910
|
+
|
|
911
|
+
Returns:
|
|
912
|
+
ot_trace.Tracer: The OpenTelemetry tracer instance.
|
|
913
|
+
"""
|
|
914
|
+
|
|
915
|
+
resource = Resource(
|
|
916
|
+
attributes={
|
|
917
|
+
SERVICE_NAME: _get_service_name(),
|
|
918
|
+
SERVICE_VERSION: os.getenv("SERVICE_VERSION", "1.0.0"),
|
|
919
|
+
"source": "agentscope_runtime-source",
|
|
920
|
+
},
|
|
921
|
+
)
|
|
922
|
+
provider = TracerProvider(resource=resource)
|
|
923
|
+
if _str_to_bool(os.getenv("TRACE_ENABLE_REPORT", "false")):
|
|
924
|
+
span_exporter = BatchSpanProcessor(
|
|
925
|
+
OTLPSpanGrpcExporter(
|
|
926
|
+
endpoint=os.getenv("TRACE_ENDPOINT", ""),
|
|
927
|
+
headers=f"Authentication="
|
|
928
|
+
f"{os.getenv('TRACE_AUTHENTICATION', '')}",
|
|
929
|
+
),
|
|
930
|
+
)
|
|
931
|
+
provider.add_span_processor(span_exporter)
|
|
932
|
+
|
|
933
|
+
if _str_to_bool(os.getenv("TRACE_ENABLE_DEBUG", "false")):
|
|
934
|
+
span_logger = BatchSpanProcessor(ConsoleSpanExporter())
|
|
935
|
+
provider.add_span_processor(span_logger)
|
|
936
|
+
|
|
937
|
+
tracer = ot_trace.get_tracer(
|
|
938
|
+
"agentscope_runtime",
|
|
939
|
+
tracer_provider=provider,
|
|
940
|
+
)
|
|
941
|
+
return tracer
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
_ot_tracer = _get_ot_tracer()
|
|
945
|
+
|
|
946
|
+
_tracer = _get_tracer()
|