veadk-python 0.2.27__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.
- veadk/__init__.py +37 -0
- veadk/a2a/__init__.py +13 -0
- veadk/a2a/agent_card.py +45 -0
- veadk/a2a/remote_ve_agent.py +390 -0
- veadk/a2a/utils/__init__.py +13 -0
- veadk/a2a/utils/agent_to_a2a.py +170 -0
- veadk/a2a/ve_a2a_server.py +93 -0
- veadk/a2a/ve_agent_executor.py +78 -0
- veadk/a2a/ve_middlewares.py +313 -0
- veadk/a2a/ve_task_store.py +37 -0
- veadk/agent.py +402 -0
- veadk/agent_builder.py +93 -0
- veadk/agents/loop_agent.py +68 -0
- veadk/agents/parallel_agent.py +72 -0
- veadk/agents/sequential_agent.py +64 -0
- veadk/auth/__init__.py +13 -0
- veadk/auth/base_auth.py +22 -0
- veadk/auth/ve_credential_service.py +203 -0
- veadk/auth/veauth/__init__.py +13 -0
- veadk/auth/veauth/apmplus_veauth.py +58 -0
- veadk/auth/veauth/ark_veauth.py +75 -0
- veadk/auth/veauth/base_veauth.py +50 -0
- veadk/auth/veauth/cozeloop_veauth.py +13 -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/speech_veauth.py +54 -0
- veadk/auth/veauth/utils.py +69 -0
- veadk/auth/veauth/vesearch_veauth.py +62 -0
- veadk/auth/veauth/viking_mem0_veauth.py +91 -0
- veadk/cli/__init__.py +13 -0
- veadk/cli/cli.py +58 -0
- veadk/cli/cli_clean.py +87 -0
- veadk/cli/cli_create.py +163 -0
- veadk/cli/cli_deploy.py +233 -0
- veadk/cli/cli_eval.py +215 -0
- veadk/cli/cli_init.py +214 -0
- veadk/cli/cli_kb.py +110 -0
- veadk/cli/cli_pipeline.py +285 -0
- veadk/cli/cli_prompt.py +86 -0
- veadk/cli/cli_update.py +106 -0
- veadk/cli/cli_uploadevalset.py +139 -0
- veadk/cli/cli_web.py +143 -0
- veadk/cloud/__init__.py +13 -0
- veadk/cloud/cloud_agent_engine.py +485 -0
- veadk/cloud/cloud_app.py +475 -0
- veadk/config.py +115 -0
- veadk/configs/__init__.py +13 -0
- veadk/configs/auth_configs.py +133 -0
- veadk/configs/database_configs.py +132 -0
- veadk/configs/model_configs.py +78 -0
- veadk/configs/tool_configs.py +54 -0
- veadk/configs/tracing_configs.py +110 -0
- veadk/consts.py +74 -0
- veadk/evaluation/__init__.py +17 -0
- veadk/evaluation/adk_evaluator/__init__.py +17 -0
- veadk/evaluation/adk_evaluator/adk_evaluator.py +302 -0
- veadk/evaluation/base_evaluator.py +642 -0
- veadk/evaluation/deepeval_evaluator/__init__.py +17 -0
- veadk/evaluation/deepeval_evaluator/deepeval_evaluator.py +339 -0
- veadk/evaluation/eval_set_file_loader.py +48 -0
- veadk/evaluation/eval_set_recorder.py +146 -0
- veadk/evaluation/types.py +65 -0
- veadk/evaluation/utils/prometheus.py +196 -0
- veadk/integrations/__init__.py +13 -0
- veadk/integrations/ve_apig/__init__.py +13 -0
- veadk/integrations/ve_apig/ve_apig.py +349 -0
- veadk/integrations/ve_apig/ve_apig_utils.py +332 -0
- 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/__init__.py +13 -0
- veadk/integrations/ve_cr/ve_cr.py +220 -0
- veadk/integrations/ve_faas/__init__.py +13 -0
- veadk/integrations/ve_faas/template/cookiecutter.json +15 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/__init__.py +13 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/config.yaml.example +6 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/deploy.py +106 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__init__.py +13 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/agent.py +25 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/app.py +202 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/requirements.txt +3 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/run.sh +49 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name }}/__init__.py +14 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name }}/agent.py +27 -0
- veadk/integrations/ve_faas/ve_faas.py +754 -0
- veadk/integrations/ve_faas/ve_faas_utils.py +408 -0
- veadk/integrations/ve_faas/web_template/cookiecutter.json +20 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/__init__.py +13 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/config.yaml.example +2 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/deploy.py +44 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/Dockerfile +23 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/app.py +123 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/init_db.py +46 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/models.py +36 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/requirements.txt +4 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/run.sh +21 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/css/style.css +368 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/js/admin.js +0 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/dashboard.html +21 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/edit_post.html +24 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/login.html +21 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/posts.html +53 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/base.html +45 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/index.html +29 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/post.html +14 -0
- veadk/integrations/ve_identity/__init__.py +110 -0
- veadk/integrations/ve_identity/auth_config.py +261 -0
- veadk/integrations/ve_identity/auth_mixins.py +650 -0
- veadk/integrations/ve_identity/auth_processor.py +385 -0
- veadk/integrations/ve_identity/function_tool.py +158 -0
- veadk/integrations/ve_identity/identity_client.py +864 -0
- veadk/integrations/ve_identity/mcp_tool.py +181 -0
- veadk/integrations/ve_identity/mcp_toolset.py +431 -0
- veadk/integrations/ve_identity/models.py +228 -0
- veadk/integrations/ve_identity/token_manager.py +188 -0
- veadk/integrations/ve_identity/utils.py +151 -0
- veadk/integrations/ve_prompt_pilot/__init__.py +13 -0
- veadk/integrations/ve_prompt_pilot/ve_prompt_pilot.py +85 -0
- veadk/integrations/ve_tls/__init__.py +13 -0
- veadk/integrations/ve_tls/utils.py +116 -0
- veadk/integrations/ve_tls/ve_tls.py +212 -0
- veadk/integrations/ve_tos/ve_tos.py +710 -0
- veadk/integrations/ve_viking_db_memory/__init__.py +13 -0
- veadk/integrations/ve_viking_db_memory/ve_viking_db_memory.py +308 -0
- veadk/knowledgebase/__init__.py +17 -0
- veadk/knowledgebase/backends/__init__.py +13 -0
- veadk/knowledgebase/backends/base_backend.py +72 -0
- veadk/knowledgebase/backends/in_memory_backend.py +91 -0
- veadk/knowledgebase/backends/opensearch_backend.py +162 -0
- veadk/knowledgebase/backends/redis_backend.py +172 -0
- veadk/knowledgebase/backends/utils.py +92 -0
- veadk/knowledgebase/backends/vikingdb_knowledge_backend.py +608 -0
- veadk/knowledgebase/entry.py +25 -0
- veadk/knowledgebase/knowledgebase.py +307 -0
- veadk/memory/__init__.py +35 -0
- veadk/memory/long_term_memory.py +365 -0
- veadk/memory/long_term_memory_backends/__init__.py +13 -0
- veadk/memory/long_term_memory_backends/base_backend.py +35 -0
- veadk/memory/long_term_memory_backends/in_memory_backend.py +67 -0
- veadk/memory/long_term_memory_backends/mem0_backend.py +155 -0
- veadk/memory/long_term_memory_backends/opensearch_backend.py +124 -0
- veadk/memory/long_term_memory_backends/redis_backend.py +140 -0
- veadk/memory/long_term_memory_backends/vikingdb_memory_backend.py +189 -0
- veadk/memory/short_term_memory.py +252 -0
- 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 +49 -0
- veadk/memory/short_term_memory_backends/postgresql_backend.py +49 -0
- veadk/memory/short_term_memory_backends/sqlite_backend.py +55 -0
- veadk/memory/short_term_memory_processor.py +100 -0
- veadk/processors/__init__.py +26 -0
- veadk/processors/base_run_processor.py +120 -0
- veadk/prompts/__init__.py +13 -0
- veadk/prompts/agent_default_prompt.py +30 -0
- veadk/prompts/prompt_evaluator.py +20 -0
- veadk/prompts/prompt_memory_processor.py +55 -0
- veadk/prompts/prompt_optimization.py +150 -0
- veadk/runner.py +732 -0
- veadk/tools/__init__.py +13 -0
- veadk/tools/builtin_tools/__init__.py +13 -0
- veadk/tools/builtin_tools/agent_authorization.py +94 -0
- veadk/tools/builtin_tools/generate_image.py +23 -0
- veadk/tools/builtin_tools/image_edit.py +300 -0
- veadk/tools/builtin_tools/image_generate.py +446 -0
- veadk/tools/builtin_tools/lark.py +67 -0
- veadk/tools/builtin_tools/las.py +24 -0
- veadk/tools/builtin_tools/link_reader.py +66 -0
- veadk/tools/builtin_tools/llm_shield.py +381 -0
- veadk/tools/builtin_tools/load_knowledgebase.py +97 -0
- veadk/tools/builtin_tools/mcp_router.py +29 -0
- veadk/tools/builtin_tools/run_code.py +113 -0
- veadk/tools/builtin_tools/tts.py +253 -0
- veadk/tools/builtin_tools/vesearch.py +49 -0
- veadk/tools/builtin_tools/video_generate.py +363 -0
- veadk/tools/builtin_tools/web_scraper.py +76 -0
- veadk/tools/builtin_tools/web_search.py +83 -0
- veadk/tools/demo_tools.py +58 -0
- veadk/tools/load_knowledgebase_tool.py +149 -0
- veadk/tools/sandbox/__init__.py +13 -0
- veadk/tools/sandbox/browser_sandbox.py +37 -0
- veadk/tools/sandbox/code_sandbox.py +40 -0
- veadk/tools/sandbox/computer_sandbox.py +34 -0
- veadk/tracing/__init__.py +13 -0
- veadk/tracing/base_tracer.py +58 -0
- veadk/tracing/telemetry/__init__.py +13 -0
- veadk/tracing/telemetry/attributes/attributes.py +29 -0
- veadk/tracing/telemetry/attributes/extractors/common_attributes_extractors.py +180 -0
- veadk/tracing/telemetry/attributes/extractors/llm_attributes_extractors.py +858 -0
- veadk/tracing/telemetry/attributes/extractors/tool_attributes_extractors.py +152 -0
- veadk/tracing/telemetry/attributes/extractors/types.py +164 -0
- veadk/tracing/telemetry/exporters/__init__.py +13 -0
- veadk/tracing/telemetry/exporters/apmplus_exporter.py +558 -0
- veadk/tracing/telemetry/exporters/base_exporter.py +39 -0
- veadk/tracing/telemetry/exporters/cozeloop_exporter.py +129 -0
- veadk/tracing/telemetry/exporters/inmemory_exporter.py +248 -0
- veadk/tracing/telemetry/exporters/tls_exporter.py +139 -0
- veadk/tracing/telemetry/opentelemetry_tracer.py +320 -0
- veadk/tracing/telemetry/telemetry.py +411 -0
- veadk/types.py +47 -0
- veadk/utils/__init__.py +13 -0
- veadk/utils/audio_manager.py +95 -0
- veadk/utils/auth.py +294 -0
- veadk/utils/logger.py +59 -0
- veadk/utils/mcp_utils.py +44 -0
- veadk/utils/misc.py +184 -0
- veadk/utils/patches.py +101 -0
- veadk/utils/volcengine_sign.py +205 -0
- veadk/version.py +15 -0
- veadk_python-0.2.27.dist-info/METADATA +373 -0
- veadk_python-0.2.27.dist-info/RECORD +218 -0
- veadk_python-0.2.27.dist-info/WHEEL +5 -0
- veadk_python-0.2.27.dist-info/entry_points.txt +2 -0
- veadk_python-0.2.27.dist-info/licenses/LICENSE +201 -0
- veadk_python-0.2.27.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import time
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from google.adk.agents.invocation_context import InvocationContext
|
|
20
|
+
from google.adk.events import Event
|
|
21
|
+
from google.adk.models.llm_request import LlmRequest
|
|
22
|
+
from google.adk.models.llm_response import LlmResponse
|
|
23
|
+
from google.adk.tools import BaseTool
|
|
24
|
+
from opentelemetry import metrics, trace
|
|
25
|
+
from opentelemetry import metrics as metrics_api
|
|
26
|
+
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
|
|
27
|
+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
|
28
|
+
from opentelemetry.metrics._internal import Meter
|
|
29
|
+
from opentelemetry.sdk import metrics as metrics_sdk
|
|
30
|
+
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
|
|
31
|
+
from opentelemetry.sdk.resources import Resource
|
|
32
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
33
|
+
from pydantic import BaseModel, Field
|
|
34
|
+
from typing_extensions import override
|
|
35
|
+
|
|
36
|
+
from veadk.config import settings
|
|
37
|
+
from veadk.tracing.telemetry.exporters.base_exporter import BaseExporter
|
|
38
|
+
from veadk.utils.logger import get_logger
|
|
39
|
+
|
|
40
|
+
logger = get_logger(__name__)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
_GEN_AI_CLIENT_OPERATION_DURATION_BUCKETS = [
|
|
44
|
+
0.01,
|
|
45
|
+
0.02,
|
|
46
|
+
0.04,
|
|
47
|
+
0.08,
|
|
48
|
+
0.16,
|
|
49
|
+
0.32,
|
|
50
|
+
0.64,
|
|
51
|
+
1.28,
|
|
52
|
+
2.56,
|
|
53
|
+
5.12,
|
|
54
|
+
10.24,
|
|
55
|
+
20.48,
|
|
56
|
+
40.96,
|
|
57
|
+
81.92,
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
_GEN_AI_SERVER_TIME_PER_OUTPUT_TOKEN_BUCKETS = [
|
|
61
|
+
0.01,
|
|
62
|
+
0.025,
|
|
63
|
+
0.05,
|
|
64
|
+
0.075,
|
|
65
|
+
0.1,
|
|
66
|
+
0.15,
|
|
67
|
+
0.2,
|
|
68
|
+
0.3,
|
|
69
|
+
0.4,
|
|
70
|
+
0.5,
|
|
71
|
+
0.75,
|
|
72
|
+
1.0,
|
|
73
|
+
2.5,
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
_GEN_AI_SERVER_TIME_TO_FIRST_TOKEN_BUCKETS = [
|
|
77
|
+
0.001,
|
|
78
|
+
0.005,
|
|
79
|
+
0.01,
|
|
80
|
+
0.02,
|
|
81
|
+
0.04,
|
|
82
|
+
0.06,
|
|
83
|
+
0.08,
|
|
84
|
+
0.1,
|
|
85
|
+
0.25,
|
|
86
|
+
0.5,
|
|
87
|
+
0.75,
|
|
88
|
+
1.0,
|
|
89
|
+
2.5,
|
|
90
|
+
5.0,
|
|
91
|
+
7.5,
|
|
92
|
+
10.0,
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
_GEN_AI_CLIENT_TOKEN_USAGE_BUCKETS = [
|
|
96
|
+
1,
|
|
97
|
+
4,
|
|
98
|
+
16,
|
|
99
|
+
64,
|
|
100
|
+
256,
|
|
101
|
+
1024,
|
|
102
|
+
4096,
|
|
103
|
+
16384,
|
|
104
|
+
65536,
|
|
105
|
+
262144,
|
|
106
|
+
1048576,
|
|
107
|
+
4194304,
|
|
108
|
+
16777216,
|
|
109
|
+
67108864,
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@dataclass
|
|
114
|
+
class Meters:
|
|
115
|
+
"""Metric names and identifiers for OpenTelemetry instrumentation.
|
|
116
|
+
|
|
117
|
+
This class defines standardized metric names used for LLM and agent
|
|
118
|
+
observability. The metrics follow OpenTelemetry semantic conventions
|
|
119
|
+
for generative AI operations and include custom APMPlus metrics for
|
|
120
|
+
enhanced monitoring capabilities.
|
|
121
|
+
|
|
122
|
+
Standard Gen AI Metrics:
|
|
123
|
+
- LLM_CHAT_COUNT: Counter for LLM invocation frequency
|
|
124
|
+
- LLM_TOKEN_USAGE: Histogram for token consumption analysis
|
|
125
|
+
- LLM_OPERATION_DURATION: Histogram for operation latency tracking
|
|
126
|
+
- LLM_COMPLETIONS_EXCEPTIONS: Counter for error rate monitoring
|
|
127
|
+
- Streaming metrics: Performance analysis for streaming responses
|
|
128
|
+
|
|
129
|
+
APMPlus Custom Metrics:
|
|
130
|
+
- APMPLUS_SPAN_LATENCY: Span execution time for performance analysis
|
|
131
|
+
- APMPLUS_TOOL_TOKEN_USAGE: Tool-specific token consumption tracking
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
LLM_CHAT_COUNT = "gen_ai.chat.count"
|
|
135
|
+
LLM_TOKEN_USAGE = "gen_ai.client.token.usage"
|
|
136
|
+
LLM_OPERATION_DURATION = "gen_ai.client.operation.duration"
|
|
137
|
+
LLM_COMPLETIONS_EXCEPTIONS = "gen_ai.chat_completions.exceptions"
|
|
138
|
+
LLM_STREAMING_TIME_TO_FIRST_TOKEN = (
|
|
139
|
+
"gen_ai.chat_completions.streaming_time_to_first_token"
|
|
140
|
+
)
|
|
141
|
+
LLM_STREAMING_TIME_TO_GENERATE = (
|
|
142
|
+
"gen_ai.chat_completions.streaming_time_to_generate"
|
|
143
|
+
)
|
|
144
|
+
LLM_STREAMING_TIME_PER_OUTPUT_TOKEN = (
|
|
145
|
+
"gen_ai.chat_completions.streaming_time_per_output_token"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# apmplus metrics
|
|
149
|
+
# span duration
|
|
150
|
+
APMPLUS_SPAN_LATENCY = "apmplus_span_latency"
|
|
151
|
+
# tool token usage
|
|
152
|
+
APMPLUS_TOOL_TOKEN_USAGE = "apmplus_tool_token_usage"
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class MeterUploader:
|
|
156
|
+
"""Metrics uploader for APMPlus observability platform integration.
|
|
157
|
+
|
|
158
|
+
MeterUploader manages the collection and transmission of telemetry metrics
|
|
159
|
+
to Volcengine's APMPlus platform. It creates and maintains OpenTelemetry
|
|
160
|
+
metric instruments for comprehensive agent performance monitoring.
|
|
161
|
+
|
|
162
|
+
Key Features:
|
|
163
|
+
- Automatic metric instrument creation with appropriate buckets
|
|
164
|
+
- LLM call metrics including token usage and latency
|
|
165
|
+
- Tool execution metrics for performance analysis
|
|
166
|
+
- Error tracking and exception monitoring
|
|
167
|
+
- Integration with OpenTelemetry metrics SDK
|
|
168
|
+
|
|
169
|
+
Metrics Collected:
|
|
170
|
+
- LLM invocation counts and frequencies
|
|
171
|
+
- Token consumption (input/output) with histogram distribution
|
|
172
|
+
- Operation latency with performance bucket analysis
|
|
173
|
+
- Error rates and exception details
|
|
174
|
+
- Span-level performance metrics for APMPlus dashboards
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
def __init__(
|
|
178
|
+
self, name: str, endpoint: str, headers: dict, resource_attributes: dict
|
|
179
|
+
) -> None:
|
|
180
|
+
"""Initialize the meter uploader with APMPlus configuration.
|
|
181
|
+
|
|
182
|
+
Sets up the global metrics provider, creates metric instruments,
|
|
183
|
+
and configures OTLP export to APMPlus endpoints with proper
|
|
184
|
+
resource attribution and authentication.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
name: Meter name for identification and organization
|
|
188
|
+
endpoint: APMPlus OTLP endpoint URL for metric transmission
|
|
189
|
+
headers: Authentication headers including APMPlus app key
|
|
190
|
+
resource_attributes: Service metadata for metric attribution
|
|
191
|
+
"""
|
|
192
|
+
# global_metrics_provider -> global_tracer_provider
|
|
193
|
+
# exporter -> exporter
|
|
194
|
+
# metric_reader -> processor
|
|
195
|
+
global_metrics_provider = metrics_api.get_meter_provider()
|
|
196
|
+
|
|
197
|
+
# 1. init resource
|
|
198
|
+
if hasattr(global_metrics_provider, "_sdk_config"):
|
|
199
|
+
global_resource = global_metrics_provider._sdk_config.resource # type: ignore
|
|
200
|
+
else:
|
|
201
|
+
global_resource = Resource.create()
|
|
202
|
+
|
|
203
|
+
resource = global_resource.merge(Resource.create(resource_attributes))
|
|
204
|
+
|
|
205
|
+
# 2. init exporter and reader
|
|
206
|
+
exporter = OTLPMetricExporter(endpoint=endpoint, headers=headers)
|
|
207
|
+
metric_reader = PeriodicExportingMetricReader(exporter)
|
|
208
|
+
|
|
209
|
+
metrics_api.set_meter_provider(
|
|
210
|
+
metrics_sdk.MeterProvider(metric_readers=[metric_reader], resource=resource)
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# 3. init meter
|
|
214
|
+
self.meter: Meter = metrics.get_meter(name=name)
|
|
215
|
+
|
|
216
|
+
# create meter attributes
|
|
217
|
+
self.llm_invoke_counter = self.meter.create_counter(
|
|
218
|
+
name=Meters.LLM_CHAT_COUNT,
|
|
219
|
+
description="Number of LLM invocations",
|
|
220
|
+
unit="count",
|
|
221
|
+
)
|
|
222
|
+
self.token_usage = self.meter.create_histogram(
|
|
223
|
+
name=Meters.LLM_TOKEN_USAGE,
|
|
224
|
+
description="Token consumption of LLM invocations",
|
|
225
|
+
unit="count",
|
|
226
|
+
explicit_bucket_boundaries_advisory=_GEN_AI_CLIENT_TOKEN_USAGE_BUCKETS,
|
|
227
|
+
)
|
|
228
|
+
self.duration_histogram = self.meter.create_histogram(
|
|
229
|
+
name=Meters.LLM_OPERATION_DURATION,
|
|
230
|
+
unit="s",
|
|
231
|
+
description="GenAI operation duration",
|
|
232
|
+
explicit_bucket_boundaries_advisory=_GEN_AI_CLIENT_OPERATION_DURATION_BUCKETS,
|
|
233
|
+
)
|
|
234
|
+
self.chat_exception_counter = self.meter.create_counter(
|
|
235
|
+
name=Meters.LLM_COMPLETIONS_EXCEPTIONS,
|
|
236
|
+
unit="time",
|
|
237
|
+
description="Number of exceptions occurred during chat completions",
|
|
238
|
+
)
|
|
239
|
+
self.streaming_time_to_first_token = self.meter.create_histogram(
|
|
240
|
+
name=Meters.LLM_STREAMING_TIME_TO_FIRST_TOKEN,
|
|
241
|
+
unit="s",
|
|
242
|
+
description="Time to first token in streaming chat completions",
|
|
243
|
+
explicit_bucket_boundaries_advisory=_GEN_AI_SERVER_TIME_TO_FIRST_TOKEN_BUCKETS,
|
|
244
|
+
)
|
|
245
|
+
self.streaming_time_to_generate = self.meter.create_histogram(
|
|
246
|
+
name=Meters.LLM_STREAMING_TIME_TO_GENERATE,
|
|
247
|
+
unit="s",
|
|
248
|
+
description="Time between first token and completion in streaming chat completions",
|
|
249
|
+
explicit_bucket_boundaries_advisory=_GEN_AI_CLIENT_OPERATION_DURATION_BUCKETS,
|
|
250
|
+
)
|
|
251
|
+
self.streaming_time_per_output_token = self.meter.create_histogram(
|
|
252
|
+
name=Meters.LLM_STREAMING_TIME_PER_OUTPUT_TOKEN,
|
|
253
|
+
unit="s",
|
|
254
|
+
description="Time per output token in streaming chat completions",
|
|
255
|
+
explicit_bucket_boundaries_advisory=_GEN_AI_SERVER_TIME_PER_OUTPUT_TOKEN_BUCKETS,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
# apmplus metrics for veadk dashboard
|
|
259
|
+
self.apmplus_span_latency = self.meter.create_histogram(
|
|
260
|
+
name=Meters.APMPLUS_SPAN_LATENCY,
|
|
261
|
+
description="Latency of span",
|
|
262
|
+
unit="s",
|
|
263
|
+
explicit_bucket_boundaries_advisory=_GEN_AI_CLIENT_OPERATION_DURATION_BUCKETS,
|
|
264
|
+
)
|
|
265
|
+
self.apmplus_tool_token_usage = self.meter.create_histogram(
|
|
266
|
+
name=Meters.APMPLUS_TOOL_TOKEN_USAGE,
|
|
267
|
+
description="Token consumption of APMPlus tool token",
|
|
268
|
+
unit="count",
|
|
269
|
+
explicit_bucket_boundaries_advisory=_GEN_AI_CLIENT_TOKEN_USAGE_BUCKETS,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
def record_call_llm(
|
|
273
|
+
self,
|
|
274
|
+
invocation_context: InvocationContext,
|
|
275
|
+
event_id: str,
|
|
276
|
+
llm_request: LlmRequest,
|
|
277
|
+
llm_response: LlmResponse,
|
|
278
|
+
) -> None:
|
|
279
|
+
"""Record comprehensive metrics for LLM call operations.
|
|
280
|
+
|
|
281
|
+
Captures detailed telemetry data for language model invocations
|
|
282
|
+
including token consumption, latency, and error information.
|
|
283
|
+
This data enables cost optimization, performance analysis, and
|
|
284
|
+
reliability monitoring in APMPlus dashboards.
|
|
285
|
+
|
|
286
|
+
Metrics Recorded:
|
|
287
|
+
- Invocation count with model and operation attributes
|
|
288
|
+
- Input/output token usage with separate tracking
|
|
289
|
+
- Operation duration from span timing data
|
|
290
|
+
- Error counts and exception details
|
|
291
|
+
- Span latency for performance analysis
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
invocation_context: Context with agent, session, and user information
|
|
295
|
+
event_id: Unique identifier for this LLM call event
|
|
296
|
+
llm_request: Request object with model and parameter details
|
|
297
|
+
llm_response: Response object with content and usage metadata
|
|
298
|
+
"""
|
|
299
|
+
attributes = {
|
|
300
|
+
"gen_ai_system": "volcengine",
|
|
301
|
+
"gen_ai_response_model": llm_request.model,
|
|
302
|
+
"gen_ai_operation_name": "chat",
|
|
303
|
+
"gen_ai_operation_type": "llm",
|
|
304
|
+
"stream": "false",
|
|
305
|
+
"server_address": "api.volcengine.com",
|
|
306
|
+
} # required by Volcengine APMPlus
|
|
307
|
+
|
|
308
|
+
if llm_response.usage_metadata:
|
|
309
|
+
# llm invocation number += 1
|
|
310
|
+
self.llm_invoke_counter.add(1, attributes)
|
|
311
|
+
|
|
312
|
+
# upload token usage
|
|
313
|
+
input_token = llm_response.usage_metadata.prompt_token_count
|
|
314
|
+
output_token = llm_response.usage_metadata.candidates_token_count
|
|
315
|
+
|
|
316
|
+
if input_token:
|
|
317
|
+
token_attributes = {**attributes, "gen_ai_token_type": "input"}
|
|
318
|
+
self.token_usage.record(input_token, attributes=token_attributes)
|
|
319
|
+
if output_token:
|
|
320
|
+
token_attributes = {**attributes, "gen_ai_token_type": "output"}
|
|
321
|
+
self.token_usage.record(output_token, attributes=token_attributes)
|
|
322
|
+
|
|
323
|
+
# Get llm duration
|
|
324
|
+
span = trace.get_current_span()
|
|
325
|
+
if span and hasattr(span, "start_time") and self.duration_histogram:
|
|
326
|
+
# We use span start time as the llm request start time
|
|
327
|
+
tik = span.start_time # type: ignore
|
|
328
|
+
# We use current time as the llm request end time
|
|
329
|
+
tok = time.time_ns()
|
|
330
|
+
# Calculate duration in seconds
|
|
331
|
+
duration = (tok - tik) / 1e9
|
|
332
|
+
self.duration_histogram.record(
|
|
333
|
+
duration, attributes=attributes
|
|
334
|
+
) # unit in seconds
|
|
335
|
+
|
|
336
|
+
# Get model request error
|
|
337
|
+
if llm_response.error_code and self.chat_exception_counter:
|
|
338
|
+
exception_attributes = {
|
|
339
|
+
**attributes,
|
|
340
|
+
"error_type": llm_response.error_message,
|
|
341
|
+
}
|
|
342
|
+
self.chat_exception_counter.add(1, exception_attributes)
|
|
343
|
+
|
|
344
|
+
# TODO: Get streaming time to first token
|
|
345
|
+
# time_to_frist_token = 0.1
|
|
346
|
+
# if self.streaming_time_to_first_token:
|
|
347
|
+
# self.streaming_time_to_first_token.record(
|
|
348
|
+
# time_to_frist_token, attributes=attributes
|
|
349
|
+
# )
|
|
350
|
+
|
|
351
|
+
# TODO: Get streaming time to generate
|
|
352
|
+
# time_to_generate = 1.0
|
|
353
|
+
# if self.streaming_time_to_generate:
|
|
354
|
+
# self.streaming_time_to_generate.record(
|
|
355
|
+
# time_to_generate, attributes=attributes
|
|
356
|
+
# )
|
|
357
|
+
|
|
358
|
+
# TODO: Get streaming time per output token
|
|
359
|
+
# time_per_output_token = 0.01
|
|
360
|
+
# if self.streaming_time_per_output_token:
|
|
361
|
+
# self.streaming_time_per_output_token.record(
|
|
362
|
+
# time_per_output_token, attributes=attributes
|
|
363
|
+
# )
|
|
364
|
+
|
|
365
|
+
# add span name attribute
|
|
366
|
+
span = trace.get_current_span()
|
|
367
|
+
if not span:
|
|
368
|
+
return
|
|
369
|
+
|
|
370
|
+
# record span latency
|
|
371
|
+
if hasattr(span, "start_time") and self.apmplus_span_latency:
|
|
372
|
+
# span 耗时
|
|
373
|
+
duration = (time.time_ns() - span.start_time) / 1e9 # type: ignore
|
|
374
|
+
self.apmplus_span_latency.record(duration, attributes=attributes)
|
|
375
|
+
|
|
376
|
+
def record_tool_call(
|
|
377
|
+
self,
|
|
378
|
+
tool: BaseTool,
|
|
379
|
+
args: dict[str, Any],
|
|
380
|
+
function_response_event: Event,
|
|
381
|
+
):
|
|
382
|
+
"""Record metrics for tool execution operations.
|
|
383
|
+
|
|
384
|
+
Captures performance and usage metrics for tool invocations
|
|
385
|
+
including execution latency and estimated token consumption.
|
|
386
|
+
Enables monitoring of tool performance and resource usage patterns.
|
|
387
|
+
|
|
388
|
+
Metrics Recorded:
|
|
389
|
+
- Tool execution latency from span timing
|
|
390
|
+
- Input/output token estimation based on text length
|
|
391
|
+
- Tool-specific attributes for categorization
|
|
392
|
+
|
|
393
|
+
Args:
|
|
394
|
+
tool: Tool instance that was executed
|
|
395
|
+
args: Arguments passed to the tool function
|
|
396
|
+
function_response_event: Event containing execution results
|
|
397
|
+
"""
|
|
398
|
+
logger.debug(f"Record tool call work in progress. Tool: {tool.name}")
|
|
399
|
+
span = trace.get_current_span()
|
|
400
|
+
if not span:
|
|
401
|
+
return
|
|
402
|
+
operation_type = "tool"
|
|
403
|
+
operation_name = tool.name
|
|
404
|
+
operation_backend = ""
|
|
405
|
+
if hasattr(tool, "custom_metadata") and tool.custom_metadata:
|
|
406
|
+
operation_backend = tool.custom_metadata.get("backend", "")
|
|
407
|
+
|
|
408
|
+
attributes = {
|
|
409
|
+
"gen_ai_operation_name": operation_name,
|
|
410
|
+
"gen_ai_operation_type": operation_type,
|
|
411
|
+
"gen_ai_operation_backend": operation_backend,
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if hasattr(span, "start_time") and self.apmplus_span_latency:
|
|
415
|
+
# span 耗时
|
|
416
|
+
duration = (time.time_ns() - span.start_time) / 1e9 # type: ignore
|
|
417
|
+
self.apmplus_span_latency.record(duration, attributes=attributes)
|
|
418
|
+
|
|
419
|
+
if self.apmplus_tool_token_usage and hasattr(span, "attributes"):
|
|
420
|
+
tool_input = span.attributes["gen_ai.tool.input"]
|
|
421
|
+
tool_token_usage_input = (
|
|
422
|
+
len(tool_input) / 4
|
|
423
|
+
) # tool token 数量,使用文本长度/4
|
|
424
|
+
input_tool_token_attributes = {**attributes, "token_type": "input"}
|
|
425
|
+
self.apmplus_tool_token_usage.record(
|
|
426
|
+
tool_token_usage_input, attributes=input_tool_token_attributes
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
tool_output = span.attributes["gen_ai.tool.output"]
|
|
430
|
+
tool_token_usage_output = (
|
|
431
|
+
len(tool_output) / 4
|
|
432
|
+
) # tool token 数量,使用文本长度/4
|
|
433
|
+
output_tool_token_attributes = {**attributes, "token_type": "output"}
|
|
434
|
+
self.apmplus_tool_token_usage.record(
|
|
435
|
+
tool_token_usage_output, attributes=output_tool_token_attributes
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
class APMPlusExporterConfig(BaseModel):
|
|
440
|
+
"""Configuration model for APMPlus exporter settings.
|
|
441
|
+
|
|
442
|
+
Manages connection parameters and authentication details for
|
|
443
|
+
integrating with Volcengine's APMPlus observability platform.
|
|
444
|
+
|
|
445
|
+
Attributes:
|
|
446
|
+
endpoint: OTLP endpoint URL for APMPlus data ingestion
|
|
447
|
+
app_key: Authentication key for APMPlus API access
|
|
448
|
+
service_name: Service identifier displayed in APMPlus interface
|
|
449
|
+
"""
|
|
450
|
+
|
|
451
|
+
endpoint: str = Field(
|
|
452
|
+
default_factory=lambda: settings.apmplus_config.otel_exporter_endpoint,
|
|
453
|
+
)
|
|
454
|
+
app_key: str = Field(
|
|
455
|
+
default_factory=lambda: settings.apmplus_config.otel_exporter_api_key,
|
|
456
|
+
)
|
|
457
|
+
service_name: str = Field(
|
|
458
|
+
default_factory=lambda: settings.apmplus_config.otel_exporter_service_name,
|
|
459
|
+
description="Service name shown in APMPlus frontend.",
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
class APMPlusExporter(BaseExporter):
|
|
464
|
+
"""OpenTelemetry exporter for Volcengine APMPlus observability platform.
|
|
465
|
+
|
|
466
|
+
APMPlusExporter provides comprehensive integration with Volcengine's APMPlus
|
|
467
|
+
platform, enabling advanced observability for VeADK agents. It combines
|
|
468
|
+
distributed tracing with detailed metrics collection for complete visibility
|
|
469
|
+
into agent performance, costs, and reliability.
|
|
470
|
+
|
|
471
|
+
Key Capabilities:
|
|
472
|
+
- OTLP-based span export to APMPlus with authentication
|
|
473
|
+
- Comprehensive metrics collection for LLM and tool operations
|
|
474
|
+
- Automatic resource attribution with service identification
|
|
475
|
+
- Cost tracking through detailed token usage metrics
|
|
476
|
+
- Performance monitoring with latency histograms
|
|
477
|
+
- Error tracking and exception monitoring
|
|
478
|
+
|
|
479
|
+
Configuration:
|
|
480
|
+
The exporter uses VeADK settings for automatic configuration but
|
|
481
|
+
can be customized with explicit parameters. Authentication is
|
|
482
|
+
handled through APMPlus app keys in request headers.
|
|
483
|
+
|
|
484
|
+
Examples:
|
|
485
|
+
Basic usage with default settings:
|
|
486
|
+
```python
|
|
487
|
+
exporter = APMPlusExporter()
|
|
488
|
+
tracer = OpentelemetryTracer(exporters=[exporter])
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
Note:
|
|
492
|
+
- Requires valid APMPlus app key for authentication
|
|
493
|
+
- Endpoint should point to APMPlus OTLP ingestion service
|
|
494
|
+
- Service name appears in APMPlus dashboards for identification
|
|
495
|
+
- Metrics and spans are automatically correlated by trace context
|
|
496
|
+
- Supports both development and production environments
|
|
497
|
+
"""
|
|
498
|
+
|
|
499
|
+
config: APMPlusExporterConfig = Field(default_factory=APMPlusExporterConfig)
|
|
500
|
+
|
|
501
|
+
def model_post_init(self, context: Any) -> None:
|
|
502
|
+
"""Initialize APMPlus exporter components after model construction.
|
|
503
|
+
|
|
504
|
+
Sets up OTLP span exporter, batch processor, and meter uploader
|
|
505
|
+
with proper authentication and resource attribution for APMPlus
|
|
506
|
+
integration.
|
|
507
|
+
|
|
508
|
+
Components Initialized:
|
|
509
|
+
- OTLP span exporter with APMPlus endpoint and authentication
|
|
510
|
+
- Batch span processor for efficient data transmission
|
|
511
|
+
- Meter uploader for comprehensive metrics collection
|
|
512
|
+
- Resource attributes for service identification
|
|
513
|
+
"""
|
|
514
|
+
logger.info(f"APMPlusExporter sevice name: {self.config.service_name}")
|
|
515
|
+
|
|
516
|
+
headers = {
|
|
517
|
+
"x-byteapm-appkey": self.config.app_key,
|
|
518
|
+
}
|
|
519
|
+
self.headers |= headers
|
|
520
|
+
|
|
521
|
+
resource_attributes = {
|
|
522
|
+
"service.name": self.config.service_name,
|
|
523
|
+
}
|
|
524
|
+
self.resource_attributes |= resource_attributes
|
|
525
|
+
|
|
526
|
+
self._exporter = OTLPSpanExporter(
|
|
527
|
+
endpoint=self.config.endpoint, insecure=True, headers=self.headers
|
|
528
|
+
)
|
|
529
|
+
self.processor = BatchSpanProcessor(self._exporter)
|
|
530
|
+
|
|
531
|
+
self.meter_uploader = MeterUploader(
|
|
532
|
+
name="apmplus_meter",
|
|
533
|
+
endpoint=self.config.endpoint,
|
|
534
|
+
headers=self.headers,
|
|
535
|
+
resource_attributes=self.resource_attributes,
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
@override
|
|
539
|
+
def export(self) -> None:
|
|
540
|
+
"""Force immediate export of pending telemetry data to APMPlus.
|
|
541
|
+
|
|
542
|
+
Triggers force flush on the OTLP span exporter to ensure all
|
|
543
|
+
buffered span data is immediately transmitted to APMPlus for
|
|
544
|
+
real-time observability and debugging.
|
|
545
|
+
|
|
546
|
+
Operations:
|
|
547
|
+
- Forces flush of span exporter if initialized
|
|
548
|
+
- Logs export status and configuration details
|
|
549
|
+
- Handles cases where exporter is not properly initialized
|
|
550
|
+
"""
|
|
551
|
+
if self._exporter:
|
|
552
|
+
self._exporter.force_flush()
|
|
553
|
+
|
|
554
|
+
logger.info(
|
|
555
|
+
f"APMPlusExporter exports data to {self.config.endpoint}, service name: {self.config.service_name}"
|
|
556
|
+
)
|
|
557
|
+
else:
|
|
558
|
+
logger.warning("APMPlusExporter internal exporter is not initialized.")
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from opentelemetry.sdk.trace import SpanProcessor
|
|
16
|
+
from opentelemetry.sdk.trace.export import SpanExporter
|
|
17
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BaseExporter(BaseModel):
|
|
21
|
+
"""Abstract base class for OpenTelemetry span exporters in VeADK tracing system.
|
|
22
|
+
|
|
23
|
+
BaseExporter provides the foundation for implementing custom telemetry data
|
|
24
|
+
exporters that send span data to various observability platforms. It defines
|
|
25
|
+
the common interface and configuration structure that all concrete exporters
|
|
26
|
+
must follow.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow")
|
|
30
|
+
|
|
31
|
+
resource_attributes: dict = Field(default_factory=dict)
|
|
32
|
+
headers: dict = Field(default_factory=dict)
|
|
33
|
+
|
|
34
|
+
_exporter: SpanExporter | None = None
|
|
35
|
+
processor: SpanProcessor | None = None
|
|
36
|
+
|
|
37
|
+
def export(self) -> None:
|
|
38
|
+
"""Force export of telemetry data."""
|
|
39
|
+
pass
|