veadk-python 0.2.2__py3-none-any.whl → 0.2.4__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.

Files changed (138) hide show
  1. veadk/agent.py +3 -13
  2. veadk/agents/loop_agent.py +55 -0
  3. veadk/agents/parallel_agent.py +60 -0
  4. veadk/agents/sequential_agent.py +55 -0
  5. veadk/cli/cli_deploy.py +11 -0
  6. veadk/cli/cli_web.py +27 -0
  7. veadk/evaluation/adk_evaluator/__init__.py +4 -0
  8. veadk/evaluation/adk_evaluator/adk_evaluator.py +170 -217
  9. veadk/evaluation/base_evaluator.py +26 -20
  10. veadk/evaluation/deepeval_evaluator/deepeval_evaluator.py +8 -5
  11. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/app.py +37 -7
  12. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/run.sh +2 -6
  13. veadk/integrations/ve_faas/ve_faas.py +5 -1
  14. veadk/runner.py +55 -5
  15. veadk/tracing/base_tracer.py +25 -200
  16. veadk/tracing/telemetry/{metrics/__init__.py → attributes/attributes.py} +16 -0
  17. veadk/tracing/telemetry/attributes/extractors/common_attributes_extractors.py +71 -0
  18. veadk/tracing/telemetry/attributes/extractors/llm_attributes_extractors.py +392 -0
  19. veadk/tracing/telemetry/attributes/extractors/tool_attributes_extractors.py +70 -0
  20. veadk/tracing/telemetry/attributes/extractors/types.py +75 -0
  21. veadk/tracing/telemetry/exporters/apmplus_exporter.py +97 -38
  22. veadk/tracing/telemetry/exporters/base_exporter.py +10 -10
  23. veadk/tracing/telemetry/exporters/cozeloop_exporter.py +20 -13
  24. veadk/tracing/telemetry/exporters/inmemory_exporter.py +46 -32
  25. veadk/tracing/telemetry/exporters/tls_exporter.py +18 -12
  26. veadk/tracing/telemetry/opentelemetry_tracer.py +102 -102
  27. veadk/tracing/telemetry/telemetry.py +149 -0
  28. veadk/types.py +6 -1
  29. veadk/utils/misc.py +1 -1
  30. veadk/utils/patches.py +25 -0
  31. veadk/version.py +1 -1
  32. veadk_python-0.2.4.dist-info/METADATA +345 -0
  33. veadk_python-0.2.4.dist-info/RECORD +122 -0
  34. veadk/__pycache__/__init__.cpython-310.pyc +0 -0
  35. veadk/__pycache__/agent.cpython-310.pyc +0 -0
  36. veadk/__pycache__/config.cpython-310.pyc +0 -0
  37. veadk/__pycache__/consts.cpython-310.pyc +0 -0
  38. veadk/__pycache__/runner.cpython-310.pyc +0 -0
  39. veadk/__pycache__/types.cpython-310.pyc +0 -0
  40. veadk/__pycache__/version.cpython-310.pyc +0 -0
  41. veadk/a2a/__pycache__/__init__.cpython-310.pyc +0 -0
  42. veadk/a2a/__pycache__/agent_card.cpython-310.pyc +0 -0
  43. veadk/a2a/__pycache__/remote_ve_agent.cpython-310.pyc +0 -0
  44. veadk/a2a/__pycache__/ve_a2a_server.cpython-310.pyc +0 -0
  45. veadk/a2a/__pycache__/ve_agent_executor.cpython-310.pyc +0 -0
  46. veadk/cli/__pycache__/__init__.cpython-310.pyc +0 -0
  47. veadk/cli/__pycache__/cli.cpython-310.pyc +0 -0
  48. veadk/cli/__pycache__/cli_deploy.cpython-310.pyc +0 -0
  49. veadk/cli/__pycache__/cli_init.cpython-310.pyc +0 -0
  50. veadk/cli/__pycache__/cli_prompt.cpython-310.pyc +0 -0
  51. veadk/cli/__pycache__/cli_studio.cpython-310.pyc +0 -0
  52. veadk/cli/__pycache__/cli_web.cpython-310.pyc +0 -0
  53. veadk/cli/__pycache__/main.cpython-310.pyc +0 -0
  54. veadk/cloud/__pycache__/__init__.cpython-310.pyc +0 -0
  55. veadk/cloud/__pycache__/cloud_agent_engine.cpython-310.pyc +0 -0
  56. veadk/cloud/__pycache__/cloud_app.cpython-310.pyc +0 -0
  57. veadk/database/__pycache__/__init__.cpython-310.pyc +0 -0
  58. veadk/database/__pycache__/base_database.cpython-310.pyc +0 -0
  59. veadk/database/__pycache__/database_adapter.cpython-310.pyc +0 -0
  60. veadk/database/__pycache__/database_factory.cpython-310.pyc +0 -0
  61. veadk/database/__pycache__/local_database.cpython-310.pyc +0 -0
  62. veadk/database/kv/__pycache__/__init__.cpython-310.pyc +0 -0
  63. veadk/database/relational/__pycache__/__init__.cpython-310.pyc +0 -0
  64. veadk/database/vector/__pycache__/__init__.cpython-310.pyc +0 -0
  65. veadk/database/vector/__pycache__/opensearch_vector_database.cpython-310.pyc +0 -0
  66. veadk/database/vector/__pycache__/type.cpython-310.pyc +0 -0
  67. veadk/database/viking/__pycache__/__init__.cpython-310.pyc +0 -0
  68. veadk/evaluation/__pycache__/__init__.cpython-310.pyc +0 -0
  69. veadk/evaluation/__pycache__/base_evaluator.cpython-310.pyc +0 -0
  70. veadk/evaluation/__pycache__/eval_set_file_loader.cpython-310.pyc +0 -0
  71. veadk/evaluation/__pycache__/eval_set_recorder.cpython-310.pyc +0 -0
  72. veadk/evaluation/__pycache__/types.cpython-310.pyc +0 -0
  73. veadk/evaluation/adk_evaluator/__pycache__/__init__.cpython-310.pyc +0 -0
  74. veadk/evaluation/deepeval_evaluator/__pycache__/__init__.cpython-310.pyc +0 -0
  75. veadk/evaluation/deepeval_evaluator/__pycache__/deepeval_evaluator.cpython-310.pyc +0 -0
  76. veadk/evaluation/utils/__pycache__/prometheus.cpython-310.pyc +0 -0
  77. veadk/integrations/ve_apig/__pycache__/__init__.cpython-310.pyc +0 -0
  78. veadk/integrations/ve_apig/__pycache__/apig.cpython-310.pyc +0 -0
  79. veadk/integrations/ve_apig/__pycache__/ve_apig.cpython-310.pyc +0 -0
  80. veadk/integrations/ve_faas/__pycache__/__init__.cpython-310.pyc +0 -0
  81. veadk/integrations/ve_faas/__pycache__/types.cpython-310.pyc +0 -0
  82. veadk/integrations/ve_faas/__pycache__/ve_faas.cpython-310.pyc +0 -0
  83. veadk/integrations/ve_faas/__pycache__/ve_faas_utils.cpython-310.pyc +0 -0
  84. veadk/integrations/ve_faas/__pycache__/vefaas.cpython-310.pyc +0 -0
  85. veadk/integrations/ve_faas/__pycache__/vefaas_utils.cpython-310.pyc +0 -0
  86. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__pycache__/agent.cpython-310.pyc +0 -0
  87. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__pycache__/app.cpython-310.pyc +0 -0
  88. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__pycache__/studio_app.cpython-310.pyc +0 -0
  89. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name|replace('-', '_') }}/__pycache__/__init__.cpython-310.pyc +0 -0
  90. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name|replace('-', '_') }}/__pycache__/agent.cpython-310.pyc +0 -0
  91. veadk/integrations/ve_prompt_pilot/__pycache__/__init__.cpython-310.pyc +0 -0
  92. veadk/integrations/ve_prompt_pilot/__pycache__/agentpilot.cpython-310.pyc +0 -0
  93. veadk/knowledgebase/__pycache__/__init__.cpython-310.pyc +0 -0
  94. veadk/knowledgebase/__pycache__/knowledgebase.cpython-310.pyc +0 -0
  95. veadk/knowledgebase/__pycache__/knowledgebase_database_adapter.cpython-310.pyc +0 -0
  96. veadk/memory/__pycache__/__init__.cpython-310.pyc +0 -0
  97. veadk/memory/__pycache__/long_term_memory.cpython-310.pyc +0 -0
  98. veadk/memory/__pycache__/memory_database_adapter.cpython-310.pyc +0 -0
  99. veadk/memory/__pycache__/short_term_memory.cpython-310.pyc +0 -0
  100. veadk/memory/__pycache__/short_term_memory_processor.cpython-310.pyc +0 -0
  101. veadk/prompts/__pycache__/__init__.cpython-310.pyc +0 -0
  102. veadk/prompts/__pycache__/agent_default_prompt.cpython-310.pyc +0 -0
  103. veadk/prompts/__pycache__/prompt_memory_processor.cpython-310.pyc +0 -0
  104. veadk/prompts/__pycache__/prompt_optimization.cpython-310.pyc +0 -0
  105. veadk/tools/__pycache__/__init__.cpython-310.pyc +0 -0
  106. veadk/tools/__pycache__/demo_tools.cpython-310.pyc +0 -0
  107. veadk/tools/__pycache__/load_knowledgebase_tool.cpython-310.pyc +0 -0
  108. veadk/tools/builtin_tools/__pycache__/__init__.cpython-310.pyc +0 -0
  109. veadk/tools/builtin_tools/__pycache__/lark.cpython-310.pyc +0 -0
  110. veadk/tools/builtin_tools/__pycache__/vesearch.cpython-310.pyc +0 -0
  111. veadk/tools/builtin_tools/__pycache__/web_search.cpython-310.pyc +0 -0
  112. veadk/tools/sandbox/__pycache__/__init__.cpython-310.pyc +0 -0
  113. veadk/tracing/__pycache__/__init__.cpython-310.pyc +0 -0
  114. veadk/tracing/__pycache__/base_tracer.cpython-310.pyc +0 -0
  115. veadk/tracing/telemetry/__pycache__/__init__.cpython-310.pyc +0 -0
  116. veadk/tracing/telemetry/__pycache__/opentelemetry_tracer.cpython-310.pyc +0 -0
  117. veadk/tracing/telemetry/exporters/__pycache__/__init__.cpython-310.pyc +0 -0
  118. veadk/tracing/telemetry/exporters/__pycache__/apiserver_exporter.cpython-310.pyc +0 -0
  119. veadk/tracing/telemetry/exporters/__pycache__/apmplus_exporter.cpython-310.pyc +0 -0
  120. veadk/tracing/telemetry/exporters/__pycache__/base_exporter.cpython-310.pyc +0 -0
  121. veadk/tracing/telemetry/exporters/__pycache__/cozeloop_exporter.cpython-310.pyc +0 -0
  122. veadk/tracing/telemetry/exporters/__pycache__/inmemory_exporter.cpython-310.pyc +0 -0
  123. veadk/tracing/telemetry/exporters/__pycache__/tls_exporter.cpython-310.pyc +0 -0
  124. veadk/tracing/telemetry/metrics/__pycache__/__init__.cpython-310.pyc +0 -0
  125. veadk/tracing/telemetry/metrics/__pycache__/opentelemetry_metrics.cpython-310.pyc +0 -0
  126. veadk/tracing/telemetry/metrics/opentelemetry_metrics.py +0 -73
  127. veadk/utils/__pycache__/__init__.cpython-310.pyc +0 -0
  128. veadk/utils/__pycache__/logger.cpython-310.pyc +0 -0
  129. veadk/utils/__pycache__/mcp_utils.cpython-310.pyc +0 -0
  130. veadk/utils/__pycache__/misc.cpython-310.pyc +0 -0
  131. veadk/utils/__pycache__/patches.cpython-310.pyc +0 -0
  132. veadk/utils/__pycache__/volcengine_sign.cpython-310.pyc +0 -0
  133. veadk_python-0.2.2.dist-info/METADATA +0 -144
  134. veadk_python-0.2.2.dist-info/RECORD +0 -213
  135. {veadk_python-0.2.2.dist-info → veadk_python-0.2.4.dist-info}/WHEEL +0 -0
  136. {veadk_python-0.2.2.dist-info → veadk_python-0.2.4.dist-info}/entry_points.txt +0 -0
  137. {veadk_python-0.2.2.dist-info → veadk_python-0.2.4.dist-info}/licenses/LICENSE +0 -0
  138. {veadk_python-0.2.2.dist-info → veadk_python-0.2.4.dist-info}/top_level.txt +0 -0
@@ -18,96 +18,71 @@ import json
18
18
  import time
19
19
  from typing import Any
20
20
 
21
- from openinference.instrumentation.google_adk import GoogleADKInstrumentor
22
21
  from opentelemetry import trace as trace_api
23
22
  from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
24
23
  from opentelemetry.sdk import trace as trace_sdk
25
24
  from opentelemetry.sdk.resources import Resource
26
25
  from opentelemetry.sdk.trace import TracerProvider
27
26
  from opentelemetry.sdk.trace.export import BatchSpanProcessor, SimpleSpanProcessor
28
- from pydantic import BaseModel, ConfigDict, Field
27
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
29
28
  from typing_extensions import override
30
29
 
31
30
  from veadk.tracing.base_tracer import BaseTracer
32
31
  from veadk.tracing.telemetry.exporters.apmplus_exporter import APMPlusExporter
33
32
  from veadk.tracing.telemetry.exporters.base_exporter import BaseExporter
34
33
  from veadk.tracing.telemetry.exporters.inmemory_exporter import InMemoryExporter
35
- from veadk.tracing.telemetry.metrics.opentelemetry_metrics import MeterUploader
36
34
  from veadk.utils.logger import get_logger
35
+ from veadk.utils.patches import patch_google_adk_telemetry
37
36
 
38
37
  logger = get_logger(__name__)
39
38
 
40
- DEFAULT_VEADK_TRACER_NAME = "veadk_global_tracer"
41
39
 
42
-
43
- def update_resource_attributions(provider: TracerProvider, resource_attributes: dict):
40
+ def _update_resource_attributions(
41
+ provider: TracerProvider, resource_attributes: dict
42
+ ) -> None:
44
43
  provider._resource = provider._resource.merge(Resource.create(resource_attributes))
45
44
 
46
45
 
47
46
  class OpentelemetryTracer(BaseModel, BaseTracer):
48
47
  model_config = ConfigDict(arbitrary_types_allowed=True)
49
- exporters: list[BaseExporter] = Field(
50
- default=[],
51
- description="The exporters to export spans.",
52
- )
48
+
53
49
  name: str = Field(
54
- DEFAULT_VEADK_TRACER_NAME, description="The identifier of tracer."
50
+ default="veadk_opentelemetry_tracer", description="The identifier of tracer."
55
51
  )
56
52
 
57
- app_name: str = Field(
58
- "veadk_app",
59
- description="The identifier of app.",
53
+ exporters: list[BaseExporter] = Field(
54
+ default_factory=list,
55
+ description="The exporters to export spans.",
60
56
  )
61
57
 
62
- def model_post_init(self, context: Any, /) -> None:
63
- self._processors = []
64
- self._inmemory_exporter: InMemoryExporter | None = None
58
+ @field_validator("exporters")
59
+ @classmethod
60
+ def forbid_inmemory_exporter(cls, v: list[BaseExporter]) -> list[BaseExporter]:
61
+ for e in v:
62
+ if isinstance(e, InMemoryExporter):
63
+ raise ValueError("InMemoryExporter is not allowed in exporters list")
64
+ return v
65
65
 
66
- # InMemoryExporter is a default exporter for exporting local tracing file
67
- for exporter in self.exporters:
68
- if isinstance(exporter, InMemoryExporter):
69
- self._inmemory_exporter = exporter
66
+ def model_post_init(self, context: Any) -> None:
67
+ patch_google_adk_telemetry()
68
+ self._init_global_tracer_provider()
70
69
 
71
- if self._inmemory_exporter is None:
72
- self._inmemory_exporter = InMemoryExporter()
73
- self.exporters.append(self._inmemory_exporter)
74
- # ========================================================================
75
-
76
- # Process meter-related attributes
77
- self._meter_contexts = []
78
- self._meter_uploaders = []
79
- for exporter in self.exporters:
80
- meter_context = exporter.get_meter_context()
81
- if meter_context is not None:
82
- self._meter_contexts.append(meter_context)
70
+ # GoogleADKInstrumentor().instrument()
83
71
 
84
- for meter_context in self._meter_contexts:
85
- meter_uploader = MeterUploader(meter_context)
86
- self._meter_uploaders.append(meter_uploader)
87
- # ================================
88
-
89
- # init tracer provider
90
- # VeADK operates on global OpenTelemetry provider, hence return nothing
91
- self._init_tracer_provider()
92
-
93
- # just for debug
94
- self._trace_file_path = ""
95
-
96
- GoogleADKInstrumentor().instrument()
72
+ def _init_global_tracer_provider(self) -> None:
73
+ self._processors = []
97
74
 
98
- def _init_tracer_provider(self) -> None:
99
- # set provider anyway
100
- # finally, get global provider
101
- tracer_provider = trace_sdk.TracerProvider()
102
- trace_api.set_tracer_provider(tracer_provider)
75
+ # set provider anyway, then get global provider
76
+ trace_api.set_tracer_provider(trace_sdk.TracerProvider())
103
77
  global_tracer_provider: TracerProvider = trace_api.get_tracer_provider() # type: ignore
104
78
 
105
- have_apmplus_exporter = False
106
- for processor in global_tracer_provider._active_span_processor._span_processors:
107
- if isinstance(processor, (BatchSpanProcessor, SimpleSpanProcessor)):
108
- if isinstance(processor.span_exporter, OTLPSpanExporter):
109
- if "apmplus" in processor.span_exporter._endpoint:
110
- have_apmplus_exporter = True
79
+ span_processors = global_tracer_provider._active_span_processor._span_processors
80
+ have_apmplus_exporter = any(
81
+ isinstance(p, (BatchSpanProcessor, SimpleSpanProcessor))
82
+ and isinstance(p.span_exporter, OTLPSpanExporter)
83
+ and "apmplus" in p.span_exporter._endpoint
84
+ for p in span_processors
85
+ )
111
86
 
112
87
  if have_apmplus_exporter:
113
88
  self.exporters = [
@@ -115,54 +90,83 @@ class OpentelemetryTracer(BaseModel, BaseTracer):
115
90
  ]
116
91
 
117
92
  for exporter in self.exporters:
118
- processor, resource_attributes = exporter.get_processor()
119
- if resource_attributes is not None:
120
- update_resource_attributions(
93
+ processor = exporter.processor
94
+ resource_attributes = exporter.resource_attributes
95
+
96
+ if resource_attributes:
97
+ _update_resource_attributions(
121
98
  global_tracer_provider, resource_attributes
122
99
  )
123
- global_tracer_provider.add_span_processor(processor)
124
- logger.debug(
125
- f"Add exporter `{exporter.__class__.__name__}` to OpentelemetryTracer."
100
+
101
+ if processor:
102
+ global_tracer_provider.add_span_processor(processor)
103
+ self._processors.append(processor)
104
+
105
+ logger.debug(
106
+ f"Add span processor for exporter `{exporter.__class__.__name__}` to OpentelemetryTracer."
107
+ )
108
+ else:
109
+ logger.error(
110
+ f"Add span processor for exporter `{exporter.__class__.__name__}` to OpentelemetryTracer failed."
111
+ )
112
+
113
+ self._inmemory_exporter = InMemoryExporter()
114
+ if self._inmemory_exporter.processor:
115
+ # make sure the in memory exporter processor is added at index 0
116
+ # because we use this to record all spans
117
+ global_tracer_provider._active_span_processor._span_processors = (
118
+ self._inmemory_exporter.processor,
119
+ ) + global_tracer_provider._active_span_processor._span_processors
120
+
121
+ self._processors.append(self._inmemory_exporter.processor)
122
+ else:
123
+ logger.warning(
124
+ "InMemoryExporter processor is not initialized, cannot add to OpentelemetryTracer."
126
125
  )
127
- self._processors.append(processor)
128
- logger.debug(f"Init OpentelemetryTracer with {len(self.exporters)} exporters.")
126
+
127
+ logger.info(f"Init OpentelemetryTracer with {len(self._processors)} exporters.")
128
+
129
+ @property
130
+ def trace_file_path(self) -> str:
131
+ return self._trace_file_path
132
+
133
+ @property
134
+ def trace_id(self) -> str:
135
+ try:
136
+ trace_id = hex(int(self._inmemory_exporter._exporter.trace_id))[2:] # type: ignore
137
+ return trace_id
138
+ except Exception as e:
139
+ logger.error(f"Failed to get trace_id from InMemoryExporter: {e}")
140
+ return self._trace_id
141
+
142
+ def force_export(self) -> None:
143
+ """Force to export spans in all processors."""
144
+ for processor in self._processors:
145
+ time.sleep(0.05)
146
+ processor.force_flush()
129
147
 
130
148
  @override
131
149
  def dump(
132
150
  self,
133
- user_id: str,
134
- session_id: str,
151
+ user_id: str = "unknown_user_id",
152
+ session_id: str = "unknown_session_id",
135
153
  path: str = "/tmp",
136
154
  ) -> str:
155
+ def _build_trace_file_path(path: str, user_id: str, session_id: str) -> str:
156
+ return f"{path}/{self.name}_{user_id}_{session_id}_{self.trace_id}.json"
157
+
137
158
  if not self._inmemory_exporter:
138
159
  logger.warning(
139
160
  "InMemoryExporter is not initialized. Please check your tracer exporters."
140
161
  )
141
162
  return ""
163
+ self.force_export()
142
164
 
143
- prompt_tokens = self._inmemory_exporter._real_exporter.prompt_tokens
144
- completion_tokens = self._inmemory_exporter._real_exporter.completion_tokens
145
-
146
- # upload
147
- for meter_uploader in self._meter_uploaders:
148
- meter_uploader.record(
149
- prompt_tokens=prompt_tokens, completion_tokens=completion_tokens
150
- )
151
- # clear tokens after dump
152
- self._inmemory_exporter._real_exporter.completion_tokens = []
153
- self._inmemory_exporter._real_exporter.prompt_tokens = []
154
-
155
- for processor in self._processors:
156
- time.sleep(0.05) # give some time for the exporter to upload spans
157
- processor.force_flush()
158
-
159
- spans = self._inmemory_exporter._real_exporter.get_finished_spans(
165
+ spans = self._inmemory_exporter._exporter.get_finished_spans( # type: ignore
160
166
  session_id=session_id
161
167
  )
162
- if not spans:
163
- data = []
164
- else:
165
- data = [
168
+ data = (
169
+ [
166
170
  {
167
171
  "name": s.name,
168
172
  "span_id": s.context.span_id,
@@ -174,22 +178,18 @@ class OpentelemetryTracer(BaseModel, BaseTracer):
174
178
  }
175
179
  for s in spans
176
180
  ]
181
+ if spans
182
+ else []
183
+ )
177
184
 
178
- trace_id = hex(int(self._inmemory_exporter._real_exporter.trace_id))[2:]
179
- self._trace_id = trace_id
180
- file_path = f"{path}/{self.name}_{user_id}_{session_id}_{trace_id}.json"
181
- with open(file_path, "w") as f:
182
- json.dump(data, f, indent=4)
183
-
184
- self._trace_file_path = file_path
185
+ self._trace_file_path = _build_trace_file_path(path, user_id, session_id)
186
+ with open(self._trace_file_path, "w") as f:
187
+ json.dump(
188
+ data, f, indent=4, ensure_ascii=False
189
+ ) # ensure_ascii=False to support Chinese characters
185
190
 
186
- for exporter in self.exporters:
187
- if not isinstance(exporter, InMemoryExporter):
188
- exporter.export()
189
191
  logger.info(
190
- f"OpenTelemetryTracer tracing done, trace id: {self._trace_id} (hex)"
192
+ f"OpenTelemetryTracer dumps {len(spans)} spans to {self._trace_file_path}. Trace id: {self.trace_id} (hex)"
191
193
  )
192
194
 
193
- self._spans = spans
194
- logger.info(f"OpenTelemetryTracer dumps {len(spans)} spans to {file_path}")
195
- return file_path
195
+ return self._trace_file_path
@@ -0,0 +1,149 @@
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 typing import Any
16
+
17
+ from google.adk.agents.invocation_context import InvocationContext
18
+ from google.adk.events import Event
19
+ from google.adk.models.llm_request import LlmRequest
20
+ from google.adk.models.llm_response import LlmResponse
21
+ from google.adk.tools import BaseTool
22
+ from opentelemetry import trace
23
+ from opentelemetry.sdk.trace import _Span
24
+
25
+ from veadk.tracing.telemetry.attributes.attributes import ATTRIBUTES
26
+ from veadk.tracing.telemetry.attributes.extractors.types import (
27
+ ExtractorResponse,
28
+ LLMAttributesParams,
29
+ ToolAttributesParams,
30
+ )
31
+ from veadk.utils.logger import get_logger
32
+
33
+ logger = get_logger(__name__)
34
+
35
+
36
+ def upload_metrics(
37
+ invocation_context: InvocationContext,
38
+ llm_request: LlmRequest,
39
+ llm_response: LlmResponse,
40
+ ) -> None:
41
+ from veadk.agent import Agent
42
+
43
+ if isinstance(invocation_context.agent, Agent):
44
+ tracers = invocation_context.agent.tracers
45
+ for tracer in tracers:
46
+ for exporter in getattr(tracer, "exporters", []):
47
+ if getattr(exporter, "meter_uploader", None):
48
+ exporter.meter_uploader.record(llm_request, llm_response)
49
+
50
+
51
+ def trace_send_data(): ...
52
+
53
+
54
+ def set_common_attributes(
55
+ invocation_context: InvocationContext, current_span: _Span, **kwargs
56
+ ) -> None:
57
+ from veadk.agent import Agent
58
+
59
+ if current_span.context:
60
+ current_span_id = current_span.context.trace_id
61
+ else:
62
+ logger.warning(
63
+ "Current span context is missing, failed to get `trace_id` to set common attributes."
64
+ )
65
+ return
66
+
67
+ if isinstance(invocation_context.agent, Agent):
68
+ try:
69
+ from veadk.tracing.telemetry.opentelemetry_tracer import OpentelemetryTracer
70
+
71
+ tracer: OpentelemetryTracer = invocation_context.agent.tracers[0] # type: ignore
72
+ spans = tracer._inmemory_exporter.processor.spans # # type: ignore
73
+
74
+ spans_in_current_trace = [
75
+ span
76
+ for span in spans
77
+ if span.context and span.context.trace_id == current_span_id
78
+ ]
79
+
80
+ common_attributes = ATTRIBUTES.get("common", {})
81
+ for span in spans_in_current_trace:
82
+ if span.name.startswith("invocation"):
83
+ span.set_attribute("gen_ai.operation.name", "chain")
84
+ elif span.name.startswith("agent_run"):
85
+ span.set_attribute("gen_ai.operation.name", "agent")
86
+ for attr_name, attr_extractor in common_attributes.items():
87
+ value = attr_extractor(**kwargs)
88
+ span.set_attribute(attr_name, value)
89
+ except Exception as e:
90
+ logger.error(f"Failed to set common attributes for spans: {e}")
91
+ else:
92
+ logger.warning(
93
+ "Failed to set common attributes for spans as your agent is not VeADK Agent. Skip this."
94
+ )
95
+
96
+
97
+ def trace_tool_call(
98
+ tool: BaseTool,
99
+ args: dict[str, Any],
100
+ function_response_event: Event,
101
+ ) -> None:
102
+ span = trace.get_current_span()
103
+
104
+ tool_attributes_mapping = ATTRIBUTES.get("tool", {})
105
+ params = ToolAttributesParams(tool, args, function_response_event)
106
+
107
+ for attr_name, attr_extractor in tool_attributes_mapping.items():
108
+ response: ExtractorResponse = attr_extractor(params)
109
+ ExtractorResponse.update_span(span, attr_name, response)
110
+
111
+
112
+ def trace_call_llm(
113
+ invocation_context: InvocationContext,
114
+ event_id: str,
115
+ llm_request: LlmRequest,
116
+ llm_response: LlmResponse,
117
+ ) -> None:
118
+ span = trace.get_current_span()
119
+
120
+ from veadk.agent import Agent
121
+
122
+ set_common_attributes(
123
+ invocation_context=invocation_context,
124
+ current_span=span, # type: ignore
125
+ agent_name=invocation_context.agent.name,
126
+ user_id=invocation_context.user_id,
127
+ app_name=invocation_context.app_name,
128
+ session_id=invocation_context.session.id,
129
+ model_provider=invocation_context.agent.model_provider
130
+ if isinstance(invocation_context.agent, Agent)
131
+ else "",
132
+ model_name=invocation_context.agent.model_name
133
+ if isinstance(invocation_context.agent, Agent)
134
+ else "",
135
+ )
136
+
137
+ llm_attributes_mapping = ATTRIBUTES.get("llm", {})
138
+ params = LLMAttributesParams(
139
+ invocation_context=invocation_context,
140
+ event_id=event_id,
141
+ llm_request=llm_request,
142
+ llm_response=llm_response,
143
+ )
144
+
145
+ for attr_name, attr_extractor in llm_attributes_mapping.items():
146
+ response: ExtractorResponse = attr_extractor(params)
147
+ ExtractorResponse.update_span(span, attr_name, response)
148
+
149
+ upload_metrics(invocation_context, llm_request, llm_response)
veadk/types.py CHANGED
@@ -15,6 +15,9 @@
15
15
  from pydantic import BaseModel, Field
16
16
 
17
17
  from veadk.agent import Agent
18
+ from veadk.agents.loop_agent import LoopAgent
19
+ from veadk.agents.parallel_agent import ParallelAgent
20
+ from veadk.agents.sequential_agent import SequentialAgent
18
21
  from veadk.memory.short_term_memory import ShortTermMemory
19
22
 
20
23
 
@@ -35,7 +38,9 @@ class AgentRunConfig(BaseModel):
35
38
  default="veadk_vefaas_app", description="The name of the application"
36
39
  )
37
40
 
38
- agent: Agent = Field(..., description="The root agent instance")
41
+ agent: Agent | SequentialAgent | ParallelAgent | LoopAgent = Field(
42
+ ..., description="The root agent instance"
43
+ )
39
44
 
40
45
  short_term_memory: ShortTermMemory = Field(
41
46
  default_factory=ShortTermMemory, description="The short-term memory instance"
veadk/utils/misc.py CHANGED
@@ -27,7 +27,7 @@ def read_file(file_path):
27
27
  return data
28
28
 
29
29
 
30
- def formatted_timestamp():
30
+ def formatted_timestamp() -> str:
31
31
  # YYYYMMDDHHMMSS
32
32
  return time.strftime("%Y%m%d%H%M%S", time.localtime())
33
33
 
veadk/utils/patches.py CHANGED
@@ -13,7 +13,14 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import asyncio
16
+ import sys
17
+ from typing import Callable
16
18
 
19
+ from veadk.tracing.telemetry.telemetry import (
20
+ trace_call_llm,
21
+ trace_send_data,
22
+ trace_tool_call,
23
+ )
17
24
  from veadk.utils.logger import get_logger
18
25
 
19
26
  logger = get_logger(__name__)
@@ -53,3 +60,21 @@ def patch_asyncio():
53
60
  raise
54
61
 
55
62
  CancelScope.__exit__ = patched_cancel_scope_exit
63
+
64
+
65
+ def patch_google_adk_telemetry() -> None:
66
+ trace_functions = {
67
+ "trace_tool_call": trace_tool_call,
68
+ "trace_call_llm": trace_call_llm,
69
+ "trace_send_data": trace_send_data,
70
+ }
71
+
72
+ for mod_name, mod in sys.modules.items():
73
+ if mod_name.startswith("google.adk"):
74
+ for var_name in dir(mod):
75
+ var = getattr(mod, var_name, None)
76
+ if var_name in trace_functions.keys() and isinstance(var, Callable):
77
+ setattr(mod, var_name, trace_functions[var_name])
78
+ logger.debug(
79
+ f"Patch {mod_name} {var_name} with {trace_functions[var_name]}"
80
+ )
veadk/version.py CHANGED
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- VERSION = "0.2.2"
15
+ VERSION = "0.2.4"