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.
Files changed (218) hide show
  1. veadk/__init__.py +37 -0
  2. veadk/a2a/__init__.py +13 -0
  3. veadk/a2a/agent_card.py +45 -0
  4. veadk/a2a/remote_ve_agent.py +390 -0
  5. veadk/a2a/utils/__init__.py +13 -0
  6. veadk/a2a/utils/agent_to_a2a.py +170 -0
  7. veadk/a2a/ve_a2a_server.py +93 -0
  8. veadk/a2a/ve_agent_executor.py +78 -0
  9. veadk/a2a/ve_middlewares.py +313 -0
  10. veadk/a2a/ve_task_store.py +37 -0
  11. veadk/agent.py +402 -0
  12. veadk/agent_builder.py +93 -0
  13. veadk/agents/loop_agent.py +68 -0
  14. veadk/agents/parallel_agent.py +72 -0
  15. veadk/agents/sequential_agent.py +64 -0
  16. veadk/auth/__init__.py +13 -0
  17. veadk/auth/base_auth.py +22 -0
  18. veadk/auth/ve_credential_service.py +203 -0
  19. veadk/auth/veauth/__init__.py +13 -0
  20. veadk/auth/veauth/apmplus_veauth.py +58 -0
  21. veadk/auth/veauth/ark_veauth.py +75 -0
  22. veadk/auth/veauth/base_veauth.py +50 -0
  23. veadk/auth/veauth/cozeloop_veauth.py +13 -0
  24. veadk/auth/veauth/opensearch_veauth.py +75 -0
  25. veadk/auth/veauth/postgresql_veauth.py +75 -0
  26. veadk/auth/veauth/prompt_pilot_veauth.py +60 -0
  27. veadk/auth/veauth/speech_veauth.py +54 -0
  28. veadk/auth/veauth/utils.py +69 -0
  29. veadk/auth/veauth/vesearch_veauth.py +62 -0
  30. veadk/auth/veauth/viking_mem0_veauth.py +91 -0
  31. veadk/cli/__init__.py +13 -0
  32. veadk/cli/cli.py +58 -0
  33. veadk/cli/cli_clean.py +87 -0
  34. veadk/cli/cli_create.py +163 -0
  35. veadk/cli/cli_deploy.py +233 -0
  36. veadk/cli/cli_eval.py +215 -0
  37. veadk/cli/cli_init.py +214 -0
  38. veadk/cli/cli_kb.py +110 -0
  39. veadk/cli/cli_pipeline.py +285 -0
  40. veadk/cli/cli_prompt.py +86 -0
  41. veadk/cli/cli_update.py +106 -0
  42. veadk/cli/cli_uploadevalset.py +139 -0
  43. veadk/cli/cli_web.py +143 -0
  44. veadk/cloud/__init__.py +13 -0
  45. veadk/cloud/cloud_agent_engine.py +485 -0
  46. veadk/cloud/cloud_app.py +475 -0
  47. veadk/config.py +115 -0
  48. veadk/configs/__init__.py +13 -0
  49. veadk/configs/auth_configs.py +133 -0
  50. veadk/configs/database_configs.py +132 -0
  51. veadk/configs/model_configs.py +78 -0
  52. veadk/configs/tool_configs.py +54 -0
  53. veadk/configs/tracing_configs.py +110 -0
  54. veadk/consts.py +74 -0
  55. veadk/evaluation/__init__.py +17 -0
  56. veadk/evaluation/adk_evaluator/__init__.py +17 -0
  57. veadk/evaluation/adk_evaluator/adk_evaluator.py +302 -0
  58. veadk/evaluation/base_evaluator.py +642 -0
  59. veadk/evaluation/deepeval_evaluator/__init__.py +17 -0
  60. veadk/evaluation/deepeval_evaluator/deepeval_evaluator.py +339 -0
  61. veadk/evaluation/eval_set_file_loader.py +48 -0
  62. veadk/evaluation/eval_set_recorder.py +146 -0
  63. veadk/evaluation/types.py +65 -0
  64. veadk/evaluation/utils/prometheus.py +196 -0
  65. veadk/integrations/__init__.py +13 -0
  66. veadk/integrations/ve_apig/__init__.py +13 -0
  67. veadk/integrations/ve_apig/ve_apig.py +349 -0
  68. veadk/integrations/ve_apig/ve_apig_utils.py +332 -0
  69. veadk/integrations/ve_code_pipeline/__init__.py +13 -0
  70. veadk/integrations/ve_code_pipeline/ve_code_pipeline.py +431 -0
  71. veadk/integrations/ve_cozeloop/__init__.py +13 -0
  72. veadk/integrations/ve_cozeloop/ve_cozeloop.py +96 -0
  73. veadk/integrations/ve_cr/__init__.py +13 -0
  74. veadk/integrations/ve_cr/ve_cr.py +220 -0
  75. veadk/integrations/ve_faas/__init__.py +13 -0
  76. veadk/integrations/ve_faas/template/cookiecutter.json +15 -0
  77. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/__init__.py +13 -0
  78. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
  79. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/config.yaml.example +6 -0
  80. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/deploy.py +106 -0
  81. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__init__.py +13 -0
  82. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/agent.py +25 -0
  83. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/app.py +202 -0
  84. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/requirements.txt +3 -0
  85. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/run.sh +49 -0
  86. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name }}/__init__.py +14 -0
  87. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name }}/agent.py +27 -0
  88. veadk/integrations/ve_faas/ve_faas.py +754 -0
  89. veadk/integrations/ve_faas/ve_faas_utils.py +408 -0
  90. veadk/integrations/ve_faas/web_template/cookiecutter.json +20 -0
  91. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/__init__.py +13 -0
  92. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
  93. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/config.yaml.example +2 -0
  94. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/deploy.py +44 -0
  95. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/Dockerfile +23 -0
  96. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/app.py +123 -0
  97. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/init_db.py +46 -0
  98. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/models.py +36 -0
  99. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/requirements.txt +4 -0
  100. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/run.sh +21 -0
  101. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/css/style.css +368 -0
  102. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/js/admin.js +0 -0
  103. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/dashboard.html +21 -0
  104. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/edit_post.html +24 -0
  105. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/login.html +21 -0
  106. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/posts.html +53 -0
  107. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/base.html +45 -0
  108. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/index.html +29 -0
  109. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/post.html +14 -0
  110. veadk/integrations/ve_identity/__init__.py +110 -0
  111. veadk/integrations/ve_identity/auth_config.py +261 -0
  112. veadk/integrations/ve_identity/auth_mixins.py +650 -0
  113. veadk/integrations/ve_identity/auth_processor.py +385 -0
  114. veadk/integrations/ve_identity/function_tool.py +158 -0
  115. veadk/integrations/ve_identity/identity_client.py +864 -0
  116. veadk/integrations/ve_identity/mcp_tool.py +181 -0
  117. veadk/integrations/ve_identity/mcp_toolset.py +431 -0
  118. veadk/integrations/ve_identity/models.py +228 -0
  119. veadk/integrations/ve_identity/token_manager.py +188 -0
  120. veadk/integrations/ve_identity/utils.py +151 -0
  121. veadk/integrations/ve_prompt_pilot/__init__.py +13 -0
  122. veadk/integrations/ve_prompt_pilot/ve_prompt_pilot.py +85 -0
  123. veadk/integrations/ve_tls/__init__.py +13 -0
  124. veadk/integrations/ve_tls/utils.py +116 -0
  125. veadk/integrations/ve_tls/ve_tls.py +212 -0
  126. veadk/integrations/ve_tos/ve_tos.py +710 -0
  127. veadk/integrations/ve_viking_db_memory/__init__.py +13 -0
  128. veadk/integrations/ve_viking_db_memory/ve_viking_db_memory.py +308 -0
  129. veadk/knowledgebase/__init__.py +17 -0
  130. veadk/knowledgebase/backends/__init__.py +13 -0
  131. veadk/knowledgebase/backends/base_backend.py +72 -0
  132. veadk/knowledgebase/backends/in_memory_backend.py +91 -0
  133. veadk/knowledgebase/backends/opensearch_backend.py +162 -0
  134. veadk/knowledgebase/backends/redis_backend.py +172 -0
  135. veadk/knowledgebase/backends/utils.py +92 -0
  136. veadk/knowledgebase/backends/vikingdb_knowledge_backend.py +608 -0
  137. veadk/knowledgebase/entry.py +25 -0
  138. veadk/knowledgebase/knowledgebase.py +307 -0
  139. veadk/memory/__init__.py +35 -0
  140. veadk/memory/long_term_memory.py +365 -0
  141. veadk/memory/long_term_memory_backends/__init__.py +13 -0
  142. veadk/memory/long_term_memory_backends/base_backend.py +35 -0
  143. veadk/memory/long_term_memory_backends/in_memory_backend.py +67 -0
  144. veadk/memory/long_term_memory_backends/mem0_backend.py +155 -0
  145. veadk/memory/long_term_memory_backends/opensearch_backend.py +124 -0
  146. veadk/memory/long_term_memory_backends/redis_backend.py +140 -0
  147. veadk/memory/long_term_memory_backends/vikingdb_memory_backend.py +189 -0
  148. veadk/memory/short_term_memory.py +252 -0
  149. veadk/memory/short_term_memory_backends/__init__.py +13 -0
  150. veadk/memory/short_term_memory_backends/base_backend.py +31 -0
  151. veadk/memory/short_term_memory_backends/mysql_backend.py +49 -0
  152. veadk/memory/short_term_memory_backends/postgresql_backend.py +49 -0
  153. veadk/memory/short_term_memory_backends/sqlite_backend.py +55 -0
  154. veadk/memory/short_term_memory_processor.py +100 -0
  155. veadk/processors/__init__.py +26 -0
  156. veadk/processors/base_run_processor.py +120 -0
  157. veadk/prompts/__init__.py +13 -0
  158. veadk/prompts/agent_default_prompt.py +30 -0
  159. veadk/prompts/prompt_evaluator.py +20 -0
  160. veadk/prompts/prompt_memory_processor.py +55 -0
  161. veadk/prompts/prompt_optimization.py +150 -0
  162. veadk/runner.py +732 -0
  163. veadk/tools/__init__.py +13 -0
  164. veadk/tools/builtin_tools/__init__.py +13 -0
  165. veadk/tools/builtin_tools/agent_authorization.py +94 -0
  166. veadk/tools/builtin_tools/generate_image.py +23 -0
  167. veadk/tools/builtin_tools/image_edit.py +300 -0
  168. veadk/tools/builtin_tools/image_generate.py +446 -0
  169. veadk/tools/builtin_tools/lark.py +67 -0
  170. veadk/tools/builtin_tools/las.py +24 -0
  171. veadk/tools/builtin_tools/link_reader.py +66 -0
  172. veadk/tools/builtin_tools/llm_shield.py +381 -0
  173. veadk/tools/builtin_tools/load_knowledgebase.py +97 -0
  174. veadk/tools/builtin_tools/mcp_router.py +29 -0
  175. veadk/tools/builtin_tools/run_code.py +113 -0
  176. veadk/tools/builtin_tools/tts.py +253 -0
  177. veadk/tools/builtin_tools/vesearch.py +49 -0
  178. veadk/tools/builtin_tools/video_generate.py +363 -0
  179. veadk/tools/builtin_tools/web_scraper.py +76 -0
  180. veadk/tools/builtin_tools/web_search.py +83 -0
  181. veadk/tools/demo_tools.py +58 -0
  182. veadk/tools/load_knowledgebase_tool.py +149 -0
  183. veadk/tools/sandbox/__init__.py +13 -0
  184. veadk/tools/sandbox/browser_sandbox.py +37 -0
  185. veadk/tools/sandbox/code_sandbox.py +40 -0
  186. veadk/tools/sandbox/computer_sandbox.py +34 -0
  187. veadk/tracing/__init__.py +13 -0
  188. veadk/tracing/base_tracer.py +58 -0
  189. veadk/tracing/telemetry/__init__.py +13 -0
  190. veadk/tracing/telemetry/attributes/attributes.py +29 -0
  191. veadk/tracing/telemetry/attributes/extractors/common_attributes_extractors.py +180 -0
  192. veadk/tracing/telemetry/attributes/extractors/llm_attributes_extractors.py +858 -0
  193. veadk/tracing/telemetry/attributes/extractors/tool_attributes_extractors.py +152 -0
  194. veadk/tracing/telemetry/attributes/extractors/types.py +164 -0
  195. veadk/tracing/telemetry/exporters/__init__.py +13 -0
  196. veadk/tracing/telemetry/exporters/apmplus_exporter.py +558 -0
  197. veadk/tracing/telemetry/exporters/base_exporter.py +39 -0
  198. veadk/tracing/telemetry/exporters/cozeloop_exporter.py +129 -0
  199. veadk/tracing/telemetry/exporters/inmemory_exporter.py +248 -0
  200. veadk/tracing/telemetry/exporters/tls_exporter.py +139 -0
  201. veadk/tracing/telemetry/opentelemetry_tracer.py +320 -0
  202. veadk/tracing/telemetry/telemetry.py +411 -0
  203. veadk/types.py +47 -0
  204. veadk/utils/__init__.py +13 -0
  205. veadk/utils/audio_manager.py +95 -0
  206. veadk/utils/auth.py +294 -0
  207. veadk/utils/logger.py +59 -0
  208. veadk/utils/mcp_utils.py +44 -0
  209. veadk/utils/misc.py +184 -0
  210. veadk/utils/patches.py +101 -0
  211. veadk/utils/volcengine_sign.py +205 -0
  212. veadk/version.py +15 -0
  213. veadk_python-0.2.27.dist-info/METADATA +373 -0
  214. veadk_python-0.2.27.dist-info/RECORD +218 -0
  215. veadk_python-0.2.27.dist-info/WHEEL +5 -0
  216. veadk_python-0.2.27.dist-info/entry_points.txt +2 -0
  217. veadk_python-0.2.27.dist-info/licenses/LICENSE +201 -0
  218. 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