veadk-python 0.2.6__py3-none-any.whl → 0.2.8__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.
Potentially problematic release.
This version of veadk-python might be problematic. Click here for more details.
- veadk/agent.py +11 -18
- veadk/agent_builder.py +94 -0
- veadk/{database/__init__.py → auth/base_auth.py} +7 -2
- veadk/auth/veauth/apmplus_veauth.py +65 -0
- veadk/auth/veauth/ark_veauth.py +77 -0
- veadk/auth/veauth/base_veauth.py +50 -0
- veadk/auth/veauth/opensearch_veauth.py +75 -0
- veadk/auth/veauth/postgresql_veauth.py +75 -0
- veadk/auth/veauth/prompt_pilot_veauth.py +60 -0
- veadk/auth/veauth/vesearch_veauth.py +62 -0
- veadk/cli/cli.py +4 -0
- veadk/cli/cli_deploy.py +3 -2
- veadk/cli/cli_eval.py +160 -0
- veadk/cli/cli_init.py +1 -1
- veadk/cli/cli_pipeline.py +220 -0
- veadk/cli/cli_prompt.py +4 -4
- veadk/cli/cli_web.py +3 -1
- veadk/config.py +45 -81
- veadk/configs/database_configs.py +117 -0
- veadk/configs/model_configs.py +74 -0
- veadk/configs/tool_configs.py +42 -0
- veadk/configs/tracing_configs.py +110 -0
- veadk/consts.py +13 -1
- veadk/evaluation/base_evaluator.py +60 -44
- veadk/evaluation/deepeval_evaluator/deepeval_evaluator.py +18 -12
- veadk/evaluation/eval_set_recorder.py +2 -2
- veadk/integrations/ve_code_pipeline/__init__.py +13 -0
- veadk/integrations/ve_code_pipeline/ve_code_pipeline.py +431 -0
- veadk/integrations/ve_cozeloop/__init__.py +13 -0
- veadk/integrations/ve_cozeloop/ve_cozeloop.py +96 -0
- veadk/integrations/ve_cr/ve_cr.py +20 -5
- veadk/integrations/ve_faas/template/cookiecutter.json +1 -1
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/deploy.py +2 -2
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/agent.py +1 -1
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/run.sh +1 -5
- veadk/integrations/ve_faas/ve_faas.py +351 -36
- veadk/integrations/ve_prompt_pilot/ve_prompt_pilot.py +6 -3
- veadk/integrations/ve_tls/__init__.py +13 -0
- veadk/integrations/ve_tls/utils.py +117 -0
- veadk/integrations/ve_tls/ve_tls.py +208 -0
- veadk/integrations/ve_tos/ve_tos.py +71 -75
- veadk/knowledgebase/backends/__init__.py +13 -0
- veadk/knowledgebase/backends/base_backend.py +59 -0
- veadk/knowledgebase/backends/in_memory_backend.py +82 -0
- veadk/knowledgebase/backends/opensearch_backend.py +136 -0
- veadk/knowledgebase/backends/redis_backend.py +144 -0
- veadk/knowledgebase/backends/utils.py +91 -0
- veadk/knowledgebase/backends/vikingdb_knowledge_backend.py +412 -0
- veadk/knowledgebase/knowledgebase.py +109 -55
- veadk/memory/__init__.py +22 -0
- veadk/memory/long_term_memory.py +120 -51
- veadk/memory/long_term_memory_backends/__init__.py +13 -0
- veadk/{database/base_database.py → memory/long_term_memory_backends/base_backend.py} +10 -22
- veadk/memory/long_term_memory_backends/in_memory_backend.py +65 -0
- veadk/memory/long_term_memory_backends/opensearch_backend.py +120 -0
- veadk/memory/long_term_memory_backends/redis_backend.py +127 -0
- veadk/memory/long_term_memory_backends/vikingdb_memory_backend.py +148 -0
- veadk/memory/short_term_memory.py +80 -72
- veadk/memory/short_term_memory_backends/__init__.py +13 -0
- veadk/memory/short_term_memory_backends/base_backend.py +31 -0
- veadk/memory/short_term_memory_backends/mysql_backend.py +41 -0
- veadk/memory/short_term_memory_backends/postgresql_backend.py +41 -0
- veadk/memory/short_term_memory_backends/sqlite_backend.py +48 -0
- veadk/memory/short_term_memory_processor.py +9 -4
- veadk/runner.py +204 -247
- veadk/tools/builtin_tools/vesearch.py +2 -2
- veadk/tools/builtin_tools/video_generate.py +27 -20
- veadk/tools/builtin_tools/web_scraper.py +1 -1
- veadk/tools/builtin_tools/web_search.py +7 -7
- veadk/tools/load_knowledgebase_tool.py +1 -1
- veadk/tracing/telemetry/attributes/extractors/llm_attributes_extractors.py +20 -2
- veadk/tracing/telemetry/exporters/apmplus_exporter.py +178 -14
- veadk/tracing/telemetry/exporters/cozeloop_exporter.py +6 -9
- veadk/tracing/telemetry/exporters/inmemory_exporter.py +22 -8
- veadk/tracing/telemetry/exporters/tls_exporter.py +6 -10
- veadk/tracing/telemetry/opentelemetry_tracer.py +5 -8
- veadk/tracing/telemetry/telemetry.py +66 -60
- veadk/utils/logger.py +1 -1
- veadk/utils/misc.py +63 -0
- veadk/utils/volcengine_sign.py +6 -2
- veadk/version.py +1 -1
- {veadk_python-0.2.6.dist-info → veadk_python-0.2.8.dist-info}/METADATA +16 -3
- {veadk_python-0.2.6.dist-info → veadk_python-0.2.8.dist-info}/RECORD +93 -64
- veadk/database/database_adapter.py +0 -368
- veadk/database/database_factory.py +0 -80
- veadk/database/kv/redis_database.py +0 -159
- veadk/database/local_database.py +0 -61
- veadk/database/relational/mysql_database.py +0 -173
- veadk/database/vector/opensearch_vector_database.py +0 -263
- veadk/database/vector/type.py +0 -50
- veadk/database/viking/viking_database.py +0 -471
- veadk/database/viking/viking_memory_db.py +0 -525
- /veadk/{database/kv → auth}/__init__.py +0 -0
- /veadk/{database/relational → auth/veauth}/__init__.py +0 -0
- /veadk/{database/vector/__init__.py → auth/veauth/cozeloop_veauth.py} +0 -0
- /veadk/{database/viking → configs}/__init__.py +0 -0
- /veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{{ cookiecutter.app_name|replace('-', '_') }} → {{ cookiecutter.app_name }}}/__init__.py +0 -0
- /veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{{ cookiecutter.app_name|replace('-', '_') }} → {{ cookiecutter.app_name }}}/agent.py +0 -0
- {veadk_python-0.2.6.dist-info → veadk_python-0.2.8.dist-info}/WHEEL +0 -0
- {veadk_python-0.2.6.dist-info → veadk_python-0.2.8.dist-info}/entry_points.txt +0 -0
- {veadk_python-0.2.6.dist-info → veadk_python-0.2.8.dist-info}/licenses/LICENSE +0 -0
- {veadk_python-0.2.6.dist-info → veadk_python-0.2.8.dist-info}/top_level.txt +0 -0
|
@@ -244,8 +244,26 @@ def llm_gen_ai_messages(params: LLMAttributesParams) -> ExtractorResponse:
|
|
|
244
244
|
)
|
|
245
245
|
if part.inline_data:
|
|
246
246
|
if len(content.parts) == 1:
|
|
247
|
-
|
|
248
|
-
|
|
247
|
+
part = content.parts[0]
|
|
248
|
+
user_event["gen_ai.user.message"].update(
|
|
249
|
+
{
|
|
250
|
+
"parts.0.type": "image_url",
|
|
251
|
+
"parts.0.image_url.name": (
|
|
252
|
+
part.inline_data.display_name.split(
|
|
253
|
+
"/"
|
|
254
|
+
)[-1]
|
|
255
|
+
if part.inline_data
|
|
256
|
+
and part.inline_data.display_name
|
|
257
|
+
else "<unknown_image_name>"
|
|
258
|
+
),
|
|
259
|
+
"parts.0.image_url.url": (
|
|
260
|
+
part.inline_data.display_name
|
|
261
|
+
if part.inline_data
|
|
262
|
+
and part.inline_data.display_name
|
|
263
|
+
else "<unknown_image_url>"
|
|
264
|
+
),
|
|
265
|
+
}
|
|
266
|
+
)
|
|
249
267
|
else:
|
|
250
268
|
user_event["gen_ai.user.message"].update(
|
|
251
269
|
{
|
|
@@ -12,11 +12,14 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
+
import time
|
|
16
|
+
from dataclasses import dataclass
|
|
15
17
|
from typing import Any
|
|
16
18
|
|
|
19
|
+
from google.adk.agents.invocation_context import InvocationContext
|
|
17
20
|
from google.adk.models.llm_request import LlmRequest
|
|
18
21
|
from google.adk.models.llm_response import LlmResponse
|
|
19
|
-
from opentelemetry import metrics
|
|
22
|
+
from opentelemetry import metrics, trace
|
|
20
23
|
from opentelemetry import metrics as metrics_api
|
|
21
24
|
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
|
|
22
25
|
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
|
@@ -28,13 +31,100 @@ from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
|
28
31
|
from pydantic import BaseModel, Field
|
|
29
32
|
from typing_extensions import override
|
|
30
33
|
|
|
31
|
-
from veadk.config import
|
|
34
|
+
from veadk.config import settings
|
|
32
35
|
from veadk.tracing.telemetry.exporters.base_exporter import BaseExporter
|
|
33
36
|
from veadk.utils.logger import get_logger
|
|
34
37
|
|
|
35
38
|
logger = get_logger(__name__)
|
|
36
39
|
|
|
37
40
|
|
|
41
|
+
_GEN_AI_CLIENT_OPERATION_DURATION_BUCKETS = [
|
|
42
|
+
0.01,
|
|
43
|
+
0.02,
|
|
44
|
+
0.04,
|
|
45
|
+
0.08,
|
|
46
|
+
0.16,
|
|
47
|
+
0.32,
|
|
48
|
+
0.64,
|
|
49
|
+
1.28,
|
|
50
|
+
2.56,
|
|
51
|
+
5.12,
|
|
52
|
+
10.24,
|
|
53
|
+
20.48,
|
|
54
|
+
40.96,
|
|
55
|
+
81.92,
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
_GEN_AI_SERVER_TIME_PER_OUTPUT_TOKEN_BUCKETS = [
|
|
59
|
+
0.01,
|
|
60
|
+
0.025,
|
|
61
|
+
0.05,
|
|
62
|
+
0.075,
|
|
63
|
+
0.1,
|
|
64
|
+
0.15,
|
|
65
|
+
0.2,
|
|
66
|
+
0.3,
|
|
67
|
+
0.4,
|
|
68
|
+
0.5,
|
|
69
|
+
0.75,
|
|
70
|
+
1.0,
|
|
71
|
+
2.5,
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
_GEN_AI_SERVER_TIME_TO_FIRST_TOKEN_BUCKETS = [
|
|
75
|
+
0.001,
|
|
76
|
+
0.005,
|
|
77
|
+
0.01,
|
|
78
|
+
0.02,
|
|
79
|
+
0.04,
|
|
80
|
+
0.06,
|
|
81
|
+
0.08,
|
|
82
|
+
0.1,
|
|
83
|
+
0.25,
|
|
84
|
+
0.5,
|
|
85
|
+
0.75,
|
|
86
|
+
1.0,
|
|
87
|
+
2.5,
|
|
88
|
+
5.0,
|
|
89
|
+
7.5,
|
|
90
|
+
10.0,
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
_GEN_AI_CLIENT_TOKEN_USAGE_BUCKETS = [
|
|
94
|
+
1,
|
|
95
|
+
4,
|
|
96
|
+
16,
|
|
97
|
+
64,
|
|
98
|
+
256,
|
|
99
|
+
1024,
|
|
100
|
+
4096,
|
|
101
|
+
16384,
|
|
102
|
+
65536,
|
|
103
|
+
262144,
|
|
104
|
+
1048576,
|
|
105
|
+
4194304,
|
|
106
|
+
16777216,
|
|
107
|
+
67108864,
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@dataclass
|
|
112
|
+
class Meters:
|
|
113
|
+
LLM_CHAT_COUNT = "gen_ai.chat.count"
|
|
114
|
+
LLM_TOKEN_USAGE = "gen_ai.client.token.usage"
|
|
115
|
+
LLM_OPERATION_DURATION = "gen_ai.client.operation.duration"
|
|
116
|
+
LLM_COMPLETIONS_EXCEPTIONS = "gen_ai.chat_completions.exceptions"
|
|
117
|
+
LLM_STREAMING_TIME_TO_FIRST_TOKEN = (
|
|
118
|
+
"gen_ai.chat_completions.streaming_time_to_first_token"
|
|
119
|
+
)
|
|
120
|
+
LLM_STREAMING_TIME_TO_GENERATE = (
|
|
121
|
+
"gen_ai.chat_completions.streaming_time_to_generate"
|
|
122
|
+
)
|
|
123
|
+
LLM_STREAMING_TIME_PER_OUTPUT_TOKEN = (
|
|
124
|
+
"gen_ai.chat_completions.streaming_time_per_output_token"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
38
128
|
class MeterUploader:
|
|
39
129
|
def __init__(
|
|
40
130
|
self, name: str, endpoint: str, headers: dict, resource_attributes: dict
|
|
@@ -65,17 +155,53 @@ class MeterUploader:
|
|
|
65
155
|
|
|
66
156
|
# create meter attributes
|
|
67
157
|
self.llm_invoke_counter = self.meter.create_counter(
|
|
68
|
-
name=
|
|
158
|
+
name=Meters.LLM_CHAT_COUNT,
|
|
69
159
|
description="Number of LLM invocations",
|
|
70
160
|
unit="count",
|
|
71
161
|
)
|
|
72
162
|
self.token_usage = self.meter.create_histogram(
|
|
73
|
-
name=
|
|
163
|
+
name=Meters.LLM_TOKEN_USAGE,
|
|
74
164
|
description="Token consumption of LLM invocations",
|
|
75
165
|
unit="count",
|
|
166
|
+
explicit_bucket_boundaries_advisory=_GEN_AI_CLIENT_TOKEN_USAGE_BUCKETS,
|
|
167
|
+
)
|
|
168
|
+
self.duration_histogram = self.meter.create_histogram(
|
|
169
|
+
name=Meters.LLM_OPERATION_DURATION,
|
|
170
|
+
unit="s",
|
|
171
|
+
description="GenAI operation duration",
|
|
172
|
+
explicit_bucket_boundaries_advisory=_GEN_AI_CLIENT_OPERATION_DURATION_BUCKETS,
|
|
173
|
+
)
|
|
174
|
+
self.chat_exception_counter = self.meter.create_counter(
|
|
175
|
+
name=Meters.LLM_COMPLETIONS_EXCEPTIONS,
|
|
176
|
+
unit="time",
|
|
177
|
+
description="Number of exceptions occurred during chat completions",
|
|
178
|
+
)
|
|
179
|
+
self.streaming_time_to_first_token = self.meter.create_histogram(
|
|
180
|
+
name=Meters.LLM_STREAMING_TIME_TO_FIRST_TOKEN,
|
|
181
|
+
unit="s",
|
|
182
|
+
description="Time to first token in streaming chat completions",
|
|
183
|
+
explicit_bucket_boundaries_advisory=_GEN_AI_SERVER_TIME_TO_FIRST_TOKEN_BUCKETS,
|
|
184
|
+
)
|
|
185
|
+
self.streaming_time_to_generate = self.meter.create_histogram(
|
|
186
|
+
name=Meters.LLM_STREAMING_TIME_TO_GENERATE,
|
|
187
|
+
unit="s",
|
|
188
|
+
description="Time between first token and completion in streaming chat completions",
|
|
189
|
+
explicit_bucket_boundaries_advisory=_GEN_AI_CLIENT_OPERATION_DURATION_BUCKETS,
|
|
190
|
+
)
|
|
191
|
+
self.streaming_time_per_output_token = self.meter.create_histogram(
|
|
192
|
+
name=Meters.LLM_STREAMING_TIME_PER_OUTPUT_TOKEN,
|
|
193
|
+
unit="s",
|
|
194
|
+
description="Time per output token in streaming chat completions",
|
|
195
|
+
explicit_bucket_boundaries_advisory=_GEN_AI_SERVER_TIME_PER_OUTPUT_TOKEN_BUCKETS,
|
|
76
196
|
)
|
|
77
197
|
|
|
78
|
-
def record(
|
|
198
|
+
def record(
|
|
199
|
+
self,
|
|
200
|
+
invocation_context: InvocationContext,
|
|
201
|
+
event_id: str,
|
|
202
|
+
llm_request: LlmRequest,
|
|
203
|
+
llm_response: LlmResponse,
|
|
204
|
+
) -> None:
|
|
79
205
|
attributes = {
|
|
80
206
|
"gen_ai_system": "volcengine",
|
|
81
207
|
"gen_ai_response_model": llm_request.model,
|
|
@@ -99,22 +225,58 @@ class MeterUploader:
|
|
|
99
225
|
token_attributes = {**attributes, "gen_ai_token_type": "output"}
|
|
100
226
|
self.token_usage.record(output_token, attributes=token_attributes)
|
|
101
227
|
|
|
228
|
+
# Get llm duration
|
|
229
|
+
span = trace.get_current_span()
|
|
230
|
+
if span and hasattr(span, "start_time") and self.duration_histogram:
|
|
231
|
+
# We use span start time as the llm request start time
|
|
232
|
+
tik = span.start_time # type: ignore
|
|
233
|
+
# We use current time as the llm request end time
|
|
234
|
+
tok = time.time_ns()
|
|
235
|
+
# Calculate duration in seconds
|
|
236
|
+
duration = (tok - tik) / 1e9
|
|
237
|
+
self.duration_histogram.record(
|
|
238
|
+
duration, attributes=attributes
|
|
239
|
+
) # unit in seconds
|
|
240
|
+
|
|
241
|
+
# Get model request error
|
|
242
|
+
if llm_response.error_code and self.chat_exception_counter:
|
|
243
|
+
exception_attributes = {
|
|
244
|
+
**attributes,
|
|
245
|
+
"error_type": llm_response.error_message,
|
|
246
|
+
}
|
|
247
|
+
self.chat_exception_counter.add(1, exception_attributes)
|
|
248
|
+
|
|
249
|
+
# TODO: Get streaming time to first token
|
|
250
|
+
# time_to_frist_token = 0.1
|
|
251
|
+
# if self.streaming_time_to_first_token:
|
|
252
|
+
# self.streaming_time_to_first_token.record(
|
|
253
|
+
# time_to_frist_token, attributes=attributes
|
|
254
|
+
# )
|
|
255
|
+
|
|
256
|
+
# TODO: Get streaming time to generate
|
|
257
|
+
# time_to_generate = 1.0
|
|
258
|
+
# if self.streaming_time_to_generate:
|
|
259
|
+
# self.streaming_time_to_generate.record(
|
|
260
|
+
# time_to_generate, attributes=attributes
|
|
261
|
+
# )
|
|
262
|
+
|
|
263
|
+
# TODO: Get streaming time per output token
|
|
264
|
+
# time_per_output_token = 0.01
|
|
265
|
+
# if self.streaming_time_per_output_token:
|
|
266
|
+
# self.streaming_time_per_output_token.record(
|
|
267
|
+
# time_per_output_token, attributes=attributes
|
|
268
|
+
# )
|
|
269
|
+
|
|
102
270
|
|
|
103
271
|
class APMPlusExporterConfig(BaseModel):
|
|
104
272
|
endpoint: str = Field(
|
|
105
|
-
default_factory=lambda:
|
|
106
|
-
"OBSERVABILITY_OPENTELEMETRY_APMPLUS_ENDPOINT",
|
|
107
|
-
"http://apmplus-cn-beijing.volces.com:4317",
|
|
108
|
-
),
|
|
273
|
+
default_factory=lambda: settings.apmplus_config.otel_exporter_endpoint,
|
|
109
274
|
)
|
|
110
275
|
app_key: str = Field(
|
|
111
|
-
default_factory=lambda:
|
|
276
|
+
default_factory=lambda: settings.apmplus_config.otel_exporter_api_key,
|
|
112
277
|
)
|
|
113
278
|
service_name: str = Field(
|
|
114
|
-
default_factory=lambda:
|
|
115
|
-
"OBSERVABILITY_OPENTELEMETRY_APMPLUS_SERVICE_NAME",
|
|
116
|
-
"veadk_tracing_service",
|
|
117
|
-
),
|
|
279
|
+
default_factory=lambda: settings.apmplus_config.otel_exporter_service_name,
|
|
118
280
|
description="Service name shown in APMPlus frontend.",
|
|
119
281
|
)
|
|
120
282
|
|
|
@@ -123,6 +285,8 @@ class APMPlusExporter(BaseExporter):
|
|
|
123
285
|
config: APMPlusExporterConfig = Field(default_factory=APMPlusExporterConfig)
|
|
124
286
|
|
|
125
287
|
def model_post_init(self, context: Any) -> None:
|
|
288
|
+
logger.info(f"APMPlusExporter sevice name: {self.config.service_name}")
|
|
289
|
+
|
|
126
290
|
headers = {
|
|
127
291
|
"x-byteapm-appkey": self.config.app_key,
|
|
128
292
|
}
|
|
@@ -19,7 +19,7 @@ from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
|
19
19
|
from pydantic import BaseModel, Field
|
|
20
20
|
from typing_extensions import override
|
|
21
21
|
|
|
22
|
-
from veadk.config import
|
|
22
|
+
from veadk.config import settings
|
|
23
23
|
from veadk.tracing.telemetry.exporters.base_exporter import BaseExporter
|
|
24
24
|
from veadk.utils.logger import get_logger
|
|
25
25
|
|
|
@@ -28,18 +28,13 @@ logger = get_logger(__name__)
|
|
|
28
28
|
|
|
29
29
|
class CozeloopExporterConfig(BaseModel):
|
|
30
30
|
endpoint: str = Field(
|
|
31
|
-
default_factory=lambda:
|
|
32
|
-
"OBSERVABILITY_OPENTELEMETRY_COZELOOP_ENDPOINT",
|
|
33
|
-
"https://api.coze.cn/v1/loop/opentelemetry/v1/traces",
|
|
34
|
-
),
|
|
31
|
+
default_factory=lambda: settings.cozeloop_config.otel_exporter_endpoint,
|
|
35
32
|
)
|
|
36
33
|
space_id: str = Field(
|
|
37
|
-
default_factory=lambda:
|
|
38
|
-
"OBSERVABILITY_OPENTELEMETRY_COZELOOP_SERVICE_NAME"
|
|
39
|
-
),
|
|
34
|
+
default_factory=lambda: settings.cozeloop_config.otel_exporter_space_id,
|
|
40
35
|
)
|
|
41
36
|
token: str = Field(
|
|
42
|
-
default_factory=lambda:
|
|
37
|
+
default_factory=lambda: settings.cozeloop_config.otel_exporter_api_key,
|
|
43
38
|
)
|
|
44
39
|
|
|
45
40
|
|
|
@@ -47,6 +42,8 @@ class CozeloopExporter(BaseExporter):
|
|
|
47
42
|
config: CozeloopExporterConfig = Field(default_factory=CozeloopExporterConfig)
|
|
48
43
|
|
|
49
44
|
def model_post_init(self, context: Any) -> None:
|
|
45
|
+
logger.info(f"CozeloopExporter space ID: {self.config.space_id}")
|
|
46
|
+
|
|
50
47
|
headers = {
|
|
51
48
|
"cozeloop-workspace-id": self.config.space_id,
|
|
52
49
|
"authorization": f"Bearer {self.config.token}",
|
|
@@ -75,11 +75,22 @@ class _InMemoryExporter(export.SpanExporter):
|
|
|
75
75
|
class _InMemorySpanProcessor(export.SimpleSpanProcessor):
|
|
76
76
|
def __init__(self, exporter: _InMemoryExporter) -> None:
|
|
77
77
|
super().__init__(exporter)
|
|
78
|
-
self.spans = []
|
|
79
78
|
|
|
80
79
|
def on_start(self, span, parent_context) -> None:
|
|
81
|
-
if span.
|
|
82
|
-
|
|
80
|
+
if span.name.startswith("invocation"):
|
|
81
|
+
span.set_attribute("gen_ai.operation.name", "chain")
|
|
82
|
+
span.set_attribute("gen_ai.usage.total_tokens", 0)
|
|
83
|
+
|
|
84
|
+
ctx = set_value("invocation_span_instance", span, context=parent_context)
|
|
85
|
+
token = attach(ctx) # mount context on `invocation` root span in Google ADK
|
|
86
|
+
setattr(span, "_invocation_token", token) # for later detach
|
|
87
|
+
|
|
88
|
+
if span.name.startswith("agent_run"):
|
|
89
|
+
span.set_attribute("gen_ai.operation.name", "agent")
|
|
90
|
+
|
|
91
|
+
ctx = set_value("agent_run_span_instance", span, context=parent_context)
|
|
92
|
+
token = attach(ctx)
|
|
93
|
+
setattr(span, "_agent_run_token", token) # for later detach
|
|
83
94
|
|
|
84
95
|
def on_end(self, span: ReadableSpan) -> None:
|
|
85
96
|
if span.context:
|
|
@@ -92,8 +103,14 @@ class _InMemorySpanProcessor(export.SimpleSpanProcessor):
|
|
|
92
103
|
except Exception:
|
|
93
104
|
logger.exception("Exception while exporting Span.")
|
|
94
105
|
detach(token)
|
|
95
|
-
|
|
96
|
-
|
|
106
|
+
|
|
107
|
+
token = getattr(span, "_invocation_token", None)
|
|
108
|
+
if token:
|
|
109
|
+
detach(token)
|
|
110
|
+
|
|
111
|
+
token = getattr(span, "_agent_run_token", None)
|
|
112
|
+
if token:
|
|
113
|
+
detach(token)
|
|
97
114
|
|
|
98
115
|
|
|
99
116
|
class InMemoryExporter(BaseExporter):
|
|
@@ -106,6 +123,3 @@ class InMemoryExporter(BaseExporter):
|
|
|
106
123
|
|
|
107
124
|
self._exporter = _InMemoryExporter()
|
|
108
125
|
self.processor = _InMemorySpanProcessor(self._exporter)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
_INMEMORY_EXPORTER_INSTANCE = InMemoryExporter()
|
|
@@ -19,7 +19,7 @@ from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
|
19
19
|
from pydantic import BaseModel, Field
|
|
20
20
|
from typing_extensions import override
|
|
21
21
|
|
|
22
|
-
from veadk.config import getenv
|
|
22
|
+
from veadk.config import getenv, settings
|
|
23
23
|
from veadk.tracing.telemetry.exporters.base_exporter import BaseExporter
|
|
24
24
|
from veadk.utils.logger import get_logger
|
|
25
25
|
|
|
@@ -28,19 +28,13 @@ logger = get_logger(__name__)
|
|
|
28
28
|
|
|
29
29
|
class TLSExporterConfig(BaseModel):
|
|
30
30
|
endpoint: str = Field(
|
|
31
|
-
default_factory=lambda:
|
|
32
|
-
"OBSERVABILITY_OPENTELEMETRY_TLS_ENDPOINT",
|
|
33
|
-
"https://tls-cn-beijing.volces.com:4318/v1/traces",
|
|
34
|
-
),
|
|
31
|
+
default_factory=lambda: settings.tls_config.otel_exporter_endpoint,
|
|
35
32
|
)
|
|
36
33
|
region: str = Field(
|
|
37
|
-
default_factory=lambda:
|
|
38
|
-
"OBSERVABILITY_OPENTELEMETRY_TLS_REGION",
|
|
39
|
-
"cn-beijing",
|
|
40
|
-
),
|
|
34
|
+
default_factory=lambda: settings.tls_config.otel_exporter_region,
|
|
41
35
|
)
|
|
42
36
|
topic_id: str = Field(
|
|
43
|
-
default_factory=lambda:
|
|
37
|
+
default_factory=lambda: settings.tls_config.otel_exporter_topic_id,
|
|
44
38
|
)
|
|
45
39
|
access_key: str = Field(default_factory=lambda: getenv("VOLCENGINE_ACCESS_KEY"))
|
|
46
40
|
secret_key: str = Field(default_factory=lambda: getenv("VOLCENGINE_SECRET_KEY"))
|
|
@@ -50,6 +44,8 @@ class TLSExporter(BaseExporter):
|
|
|
50
44
|
config: TLSExporterConfig = Field(default_factory=TLSExporterConfig)
|
|
51
45
|
|
|
52
46
|
def model_post_init(self, context: Any) -> None:
|
|
47
|
+
logger.info(f"TLSExporter topic ID: {self.config.topic_id}")
|
|
48
|
+
|
|
53
49
|
headers = {
|
|
54
50
|
"x-tls-otel-tracetopic": self.config.topic_id,
|
|
55
51
|
"x-tls-otel-ak": self.config.access_key,
|
|
@@ -19,7 +19,6 @@ import time
|
|
|
19
19
|
from typing import Any
|
|
20
20
|
|
|
21
21
|
from opentelemetry import trace as trace_api
|
|
22
|
-
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
|
23
22
|
from opentelemetry.sdk import trace as trace_sdk
|
|
24
23
|
from opentelemetry.sdk.resources import Resource
|
|
25
24
|
from opentelemetry.sdk.trace import TracerProvider
|
|
@@ -30,12 +29,10 @@ from typing_extensions import override
|
|
|
30
29
|
from veadk.tracing.base_tracer import BaseTracer
|
|
31
30
|
from veadk.tracing.telemetry.exporters.apmplus_exporter import APMPlusExporter
|
|
32
31
|
from veadk.tracing.telemetry.exporters.base_exporter import BaseExporter
|
|
33
|
-
from veadk.tracing.telemetry.exporters.inmemory_exporter import
|
|
34
|
-
_INMEMORY_EXPORTER_INSTANCE,
|
|
35
|
-
InMemoryExporter,
|
|
36
|
-
)
|
|
32
|
+
from veadk.tracing.telemetry.exporters.inmemory_exporter import InMemoryExporter
|
|
37
33
|
from veadk.utils.logger import get_logger
|
|
38
34
|
from veadk.utils.patches import patch_google_adk_telemetry
|
|
35
|
+
from veadk.utils.misc import get_temp_dir
|
|
39
36
|
|
|
40
37
|
logger = get_logger(__name__)
|
|
41
38
|
|
|
@@ -88,7 +85,7 @@ class OpentelemetryTracer(BaseModel, BaseTracer):
|
|
|
88
85
|
span_processors = global_tracer_provider._active_span_processor._span_processors
|
|
89
86
|
have_apmplus_exporter = any(
|
|
90
87
|
isinstance(p, (BatchSpanProcessor, SimpleSpanProcessor))
|
|
91
|
-
and
|
|
88
|
+
and hasattr(p.span_exporter, "_endpoint")
|
|
92
89
|
and "apmplus" in p.span_exporter._endpoint
|
|
93
90
|
for p in span_processors
|
|
94
91
|
)
|
|
@@ -119,7 +116,7 @@ class OpentelemetryTracer(BaseModel, BaseTracer):
|
|
|
119
116
|
f"Add span processor for exporter `{exporter.__class__.__name__}` to OpentelemetryTracer failed."
|
|
120
117
|
)
|
|
121
118
|
|
|
122
|
-
self._inmemory_exporter =
|
|
119
|
+
self._inmemory_exporter = InMemoryExporter()
|
|
123
120
|
if self._inmemory_exporter.processor:
|
|
124
121
|
# make sure the in memory exporter processor is added at index 0
|
|
125
122
|
# because we use this to record all spans
|
|
@@ -159,7 +156,7 @@ class OpentelemetryTracer(BaseModel, BaseTracer):
|
|
|
159
156
|
self,
|
|
160
157
|
user_id: str = "unknown_user_id",
|
|
161
158
|
session_id: str = "unknown_session_id",
|
|
162
|
-
path: str =
|
|
159
|
+
path: str = get_temp_dir(),
|
|
163
160
|
) -> str:
|
|
164
161
|
def _build_trace_file_path(path: str, user_id: str, session_id: str) -> str:
|
|
165
162
|
return f"{path}/{self.name}_{user_id}_{session_id}_{self.trace_id}.json"
|
|
@@ -20,7 +20,8 @@ from google.adk.models.llm_request import LlmRequest
|
|
|
20
20
|
from google.adk.models.llm_response import LlmResponse
|
|
21
21
|
from google.adk.tools import BaseTool
|
|
22
22
|
from opentelemetry import trace
|
|
23
|
-
from opentelemetry.
|
|
23
|
+
from opentelemetry.context import get_value
|
|
24
|
+
from opentelemetry.sdk.trace import Span, _Span
|
|
24
25
|
|
|
25
26
|
from veadk.tracing.telemetry.attributes.attributes import ATTRIBUTES
|
|
26
27
|
from veadk.tracing.telemetry.attributes.extractors.types import (
|
|
@@ -28,16 +29,14 @@ from veadk.tracing.telemetry.attributes.extractors.types import (
|
|
|
28
29
|
LLMAttributesParams,
|
|
29
30
|
ToolAttributesParams,
|
|
30
31
|
)
|
|
31
|
-
from veadk.tracing.telemetry.exporters.inmemory_exporter import (
|
|
32
|
-
_INMEMORY_EXPORTER_INSTANCE,
|
|
33
|
-
)
|
|
34
32
|
from veadk.utils.logger import get_logger
|
|
35
33
|
|
|
36
34
|
logger = get_logger(__name__)
|
|
37
35
|
|
|
38
36
|
|
|
39
|
-
def
|
|
37
|
+
def _upload_metrics(
|
|
40
38
|
invocation_context: InvocationContext,
|
|
39
|
+
event_id: str,
|
|
41
40
|
llm_request: LlmRequest,
|
|
42
41
|
llm_response: LlmResponse,
|
|
43
42
|
) -> None:
|
|
@@ -48,11 +47,13 @@ def upload_metrics(
|
|
|
48
47
|
for tracer in tracers:
|
|
49
48
|
for exporter in getattr(tracer, "exporters", []):
|
|
50
49
|
if getattr(exporter, "meter_uploader", None):
|
|
51
|
-
exporter.meter_uploader.record(
|
|
50
|
+
exporter.meter_uploader.record(
|
|
51
|
+
invocation_context, event_id, llm_request, llm_response
|
|
52
|
+
)
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
def _set_agent_input_attribute(
|
|
55
|
-
span:
|
|
56
|
+
span: Span, invocation_context: InvocationContext
|
|
56
57
|
) -> None:
|
|
57
58
|
# We only save the original user input as the agent input
|
|
58
59
|
# hence once the `agent.input` has been set, we don't overwrite it
|
|
@@ -106,7 +107,7 @@ def _set_agent_input_attribute(
|
|
|
106
107
|
)
|
|
107
108
|
|
|
108
109
|
|
|
109
|
-
def _set_agent_output_attribute(span:
|
|
110
|
+
def _set_agent_output_attribute(span: Span, llm_response: LlmResponse) -> None:
|
|
110
111
|
content = llm_response.content
|
|
111
112
|
if content and content.parts:
|
|
112
113
|
for idx, part in enumerate(content.parts):
|
|
@@ -126,64 +127,64 @@ def set_common_attributes_on_model_span(
|
|
|
126
127
|
current_span: _Span,
|
|
127
128
|
**kwargs,
|
|
128
129
|
) -> None:
|
|
129
|
-
|
|
130
|
-
current_span_id = current_span.context.trace_id
|
|
131
|
-
else:
|
|
132
|
-
logger.warning(
|
|
133
|
-
"Current span context is missing, failed to get `trace_id` to set common attributes."
|
|
134
|
-
)
|
|
135
|
-
return
|
|
136
|
-
|
|
130
|
+
common_attributes = ATTRIBUTES.get("common", {})
|
|
137
131
|
try:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
132
|
+
invocation_span: Span = get_value("invocation_span_instance") # type: ignore
|
|
133
|
+
agent_run_span: Span = get_value("agent_run_span_instance") # type: ignore
|
|
134
|
+
|
|
135
|
+
if invocation_span and invocation_span.name.startswith("invocation"):
|
|
136
|
+
_set_agent_input_attribute(invocation_span, invocation_context)
|
|
137
|
+
_set_agent_output_attribute(invocation_span, llm_response)
|
|
138
|
+
for attr_name, attr_extractor in common_attributes.items():
|
|
139
|
+
value = attr_extractor(**kwargs)
|
|
140
|
+
invocation_span.set_attribute(attr_name, value)
|
|
141
|
+
|
|
142
|
+
# Calculate the token usage for the whole invocation span
|
|
143
|
+
current_step_token_usage = (
|
|
144
|
+
llm_response.usage_metadata.total_token_count
|
|
145
|
+
if llm_response.usage_metadata
|
|
146
|
+
and llm_response.usage_metadata.total_token_count
|
|
147
|
+
else 0
|
|
148
|
+
)
|
|
149
|
+
prev_total_token_usage = (
|
|
150
|
+
invocation_span.attributes["gen_ai.usage.total_tokens"]
|
|
151
|
+
if invocation_span.attributes
|
|
152
|
+
else 0
|
|
153
|
+
)
|
|
154
|
+
accumulated_total_token_usage = (
|
|
155
|
+
current_step_token_usage + int(prev_total_token_usage) # type: ignore
|
|
156
|
+
) # we can ignore this warning, cause we manually set the attribute to int before
|
|
157
|
+
invocation_span.set_attribute(
|
|
158
|
+
"gen_ai.usage.total_tokens", accumulated_total_token_usage
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
if agent_run_span and agent_run_span.name.startswith("agent_run"):
|
|
162
|
+
_set_agent_input_attribute(agent_run_span, invocation_context)
|
|
163
|
+
_set_agent_output_attribute(agent_run_span, llm_response)
|
|
164
|
+
for attr_name, attr_extractor in common_attributes.items():
|
|
165
|
+
value = attr_extractor(**kwargs)
|
|
166
|
+
agent_run_span.set_attribute(attr_name, value)
|
|
167
|
+
|
|
168
|
+
for attr_name, attr_extractor in common_attributes.items():
|
|
169
|
+
value = attr_extractor(**kwargs)
|
|
170
|
+
current_span.set_attribute(attr_name, value)
|
|
160
171
|
except Exception as e:
|
|
161
172
|
logger.error(f"Failed to set common attributes for spans: {e}")
|
|
162
173
|
|
|
163
174
|
|
|
164
175
|
def set_common_attributes_on_tool_span(current_span: _Span) -> None:
|
|
165
|
-
|
|
166
|
-
if not current_span.context:
|
|
167
|
-
logger.warning(
|
|
168
|
-
f"Get tool span's context failed. Skip setting common attributes for span {current_span.name}"
|
|
169
|
-
)
|
|
170
|
-
return
|
|
171
|
-
|
|
172
|
-
if not current_span.parent:
|
|
173
|
-
logger.warning(
|
|
174
|
-
f"Get tool span's parent failed. Skip setting common attributes for span {current_span.name}"
|
|
175
|
-
)
|
|
176
|
-
return
|
|
177
|
-
|
|
178
|
-
parent_span_id = current_span.parent.span_id
|
|
179
|
-
for span in _INMEMORY_EXPORTER_INSTANCE.processor.spans: # type: ignore
|
|
180
|
-
if span.context.span_id == parent_span_id:
|
|
181
|
-
common_attributes = ATTRIBUTES.get("common", {})
|
|
182
|
-
for attr_name in common_attributes.keys():
|
|
183
|
-
current_span.set_attribute(attr_name, span.attributes[attr_name])
|
|
176
|
+
common_attributes = ATTRIBUTES.get("common", {})
|
|
184
177
|
|
|
178
|
+
invocation_span: Span = get_value("invocation_span_instance") # type: ignore
|
|
185
179
|
|
|
186
|
-
|
|
180
|
+
for attr_name in common_attributes.keys():
|
|
181
|
+
if (
|
|
182
|
+
invocation_span
|
|
183
|
+
and invocation_span.name.startswith("invocation")
|
|
184
|
+
and invocation_span.attributes
|
|
185
|
+
and attr_name in invocation_span.attributes
|
|
186
|
+
):
|
|
187
|
+
current_span.set_attribute(attr_name, invocation_span.attributes[attr_name])
|
|
187
188
|
|
|
188
189
|
|
|
189
190
|
def trace_tool_call(
|
|
@@ -209,7 +210,7 @@ def trace_call_llm(
|
|
|
209
210
|
llm_request: LlmRequest,
|
|
210
211
|
llm_response: LlmResponse,
|
|
211
212
|
) -> None:
|
|
212
|
-
span = trace.get_current_span()
|
|
213
|
+
span: Span = trace.get_current_span() # type: ignore
|
|
213
214
|
|
|
214
215
|
from veadk.agent import Agent
|
|
215
216
|
|
|
@@ -231,6 +232,7 @@ def trace_call_llm(
|
|
|
231
232
|
span.context.trace_state.get("call_type", "")
|
|
232
233
|
if (
|
|
233
234
|
hasattr(span, "context")
|
|
235
|
+
and span.context
|
|
234
236
|
and hasattr(span.context, "trace_state")
|
|
235
237
|
and hasattr(span.context.trace_state, "get")
|
|
236
238
|
)
|
|
@@ -250,4 +252,8 @@ def trace_call_llm(
|
|
|
250
252
|
response: ExtractorResponse = attr_extractor(params)
|
|
251
253
|
ExtractorResponse.update_span(span, attr_name, response)
|
|
252
254
|
|
|
253
|
-
|
|
255
|
+
_upload_metrics(invocation_context, event_id, llm_request, llm_response)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
# Do not modify this function
|
|
259
|
+
def trace_send_data(): ...
|