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
@@ -12,10 +12,16 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from typing import Any
16
+
17
+ from google.adk.models.llm_request import LlmRequest
18
+ from google.adk.models.llm_response import LlmResponse
15
19
  from opentelemetry import metrics
20
+ from opentelemetry import metrics as metrics_api
16
21
  from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
17
22
  from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
18
- from opentelemetry.sdk.metrics import MeterProvider
23
+ from opentelemetry.metrics._internal import Meter
24
+ from opentelemetry.sdk import metrics as metrics_sdk
19
25
  from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
20
26
  from opentelemetry.sdk.resources import Resource
21
27
  from opentelemetry.sdk.trace.export import BatchSpanProcessor
@@ -24,12 +30,76 @@ from typing_extensions import override
24
30
 
25
31
  from veadk.config import getenv
26
32
  from veadk.tracing.telemetry.exporters.base_exporter import BaseExporter
27
- from veadk.tracing.telemetry.metrics.opentelemetry_metrics import MeterContext
28
33
  from veadk.utils.logger import get_logger
29
34
 
30
35
  logger = get_logger(__name__)
31
36
 
32
37
 
38
+ class MeterUploader:
39
+ def __init__(
40
+ self, name: str, endpoint: str, headers: dict, resource_attributes: dict
41
+ ) -> None:
42
+ # global_metrics_provider -> global_tracer_provider
43
+ # exporter -> exporter
44
+ # metric_reader -> processor
45
+ global_metrics_provider = metrics_api.get_meter_provider()
46
+
47
+ # 1. init resource
48
+ if hasattr(global_metrics_provider, "_sdk_config"):
49
+ global_resource = global_metrics_provider._sdk_config.resource # type: ignore
50
+ else:
51
+ global_resource = Resource.create()
52
+
53
+ resource = global_resource.merge(Resource.create(resource_attributes))
54
+
55
+ # 2. init exporter and reader
56
+ exporter = OTLPMetricExporter(endpoint=endpoint, headers=headers)
57
+ metric_reader = PeriodicExportingMetricReader(exporter)
58
+
59
+ metrics_api.set_meter_provider(
60
+ metrics_sdk.MeterProvider(metric_readers=[metric_reader], resource=resource)
61
+ )
62
+
63
+ # 3. init meter
64
+ self.meter: Meter = metrics.get_meter(name=name)
65
+
66
+ # create meter attributes
67
+ self.llm_invoke_counter = self.meter.create_counter(
68
+ name="gen_ai.chat.count",
69
+ description="Number of LLM invocations",
70
+ unit="count",
71
+ )
72
+ self.token_usage = self.meter.create_histogram(
73
+ name="gen_ai.client.token.usage",
74
+ description="Token consumption of LLM invocations",
75
+ unit="count",
76
+ )
77
+
78
+ def record(self, llm_request: LlmRequest, llm_response: LlmResponse) -> None:
79
+ attributes = {
80
+ "gen_ai_system": "volcengine",
81
+ "gen_ai_response_model": llm_request.model,
82
+ "gen_ai_operation_name": "chat_completions",
83
+ "stream": "false",
84
+ "server_address": "api.volcengine.com",
85
+ } # required by Volcengine APMPlus
86
+
87
+ if llm_response.usage_metadata:
88
+ # llm invocation number += 1
89
+ self.llm_invoke_counter.add(1, attributes)
90
+
91
+ # upload token usage
92
+ input_token = llm_response.usage_metadata.prompt_token_count
93
+ output_token = llm_response.usage_metadata.candidates_token_count
94
+
95
+ if input_token:
96
+ token_attributes = {**attributes, "gen_ai_token_type": "input"}
97
+ self.token_usage.record(input_token, attributes=token_attributes)
98
+ if output_token:
99
+ token_attributes = {**attributes, "gen_ai_token_type": "output"}
100
+ self.token_usage.record(output_token, attributes=token_attributes)
101
+
102
+
33
103
  class APMPlusExporterConfig(BaseModel):
34
104
  endpoint: str = Field(
35
105
  default_factory=lambda: getenv(
@@ -49,50 +119,39 @@ class APMPlusExporterConfig(BaseModel):
49
119
  )
50
120
 
51
121
 
52
- class APMPlusExporter(BaseModel, BaseExporter):
122
+ class APMPlusExporter(BaseExporter):
53
123
  config: APMPlusExporterConfig = Field(default_factory=APMPlusExporterConfig)
54
124
 
55
- @override
56
- def get_processor(self):
57
- resource_attributes = {
58
- "service.name": self.config.service_name,
59
- }
60
-
125
+ def model_post_init(self, context: Any) -> None:
61
126
  headers = {
62
127
  "x-byteapm-appkey": self.config.app_key,
63
128
  }
64
- exporter = OTLPSpanExporter(
65
- endpoint=self.config.endpoint, insecure=True, headers=headers
66
- )
67
- self._real_exporter = exporter
68
- processor = BatchSpanProcessor(exporter)
69
- return processor, resource_attributes
70
-
71
- def export(self):
72
- self._real_exporter.force_flush()
73
- logger.info(
74
- f"APMPlusExporter exports data to {self.config.endpoint}, service name: {self.config.service_name}"
75
- )
129
+ self.headers |= headers
76
130
 
77
- @override
78
- def get_meter_context(self) -> MeterContext:
79
131
  resource_attributes = {
80
132
  "service.name": self.config.service_name,
81
133
  }
82
- endpoint = self.config.endpoint
83
- headers = {
84
- "x-byteapm-appkey": self.config.app_key,
85
- }
134
+ self.resource_attributes |= resource_attributes
86
135
 
87
- resource = Resource.create(resource_attributes)
88
- exporter = OTLPMetricExporter(endpoint=endpoint, headers=headers)
89
- metric_reader = PeriodicExportingMetricReader(exporter)
90
- provider = MeterProvider(metric_readers=[metric_reader], resource=resource)
91
- metrics.set_meter_provider(provider)
92
- meter = metrics.get_meter("my.meter.name")
93
- meter_context = MeterContext(
94
- meter=meter,
95
- provider=provider,
96
- reader=metric_reader,
136
+ self._exporter = OTLPSpanExporter(
137
+ endpoint=self.config.endpoint, insecure=True, headers=self.headers
97
138
  )
98
- return meter_context
139
+ self.processor = BatchSpanProcessor(self._exporter)
140
+
141
+ self.meter_uploader = MeterUploader(
142
+ name="apmplus_meter",
143
+ endpoint=self.config.endpoint,
144
+ headers=self.headers,
145
+ resource_attributes=self.resource_attributes,
146
+ )
147
+
148
+ @override
149
+ def export(self) -> None:
150
+ if self._exporter:
151
+ self._exporter.force_flush()
152
+
153
+ logger.info(
154
+ f"APMPlusExporter exports data to {self.config.endpoint}, service name: {self.config.service_name}"
155
+ )
156
+ else:
157
+ logger.warning("APMPlusExporter internal exporter is not initialized.")
@@ -12,20 +12,20 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from abc import ABC, abstractmethod
16
- from typing import Any
15
+ from opentelemetry.sdk.trace import SpanProcessor
16
+ from opentelemetry.sdk.trace.export import SpanExporter
17
+ from pydantic import BaseModel, ConfigDict, Field
17
18
 
18
19
 
19
- class BaseExporter(ABC):
20
- def __init__(self) -> None:
21
- pass
20
+ class BaseExporter(BaseModel):
21
+ model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow")
22
22
 
23
- @abstractmethod
24
- def get_processor(self) -> Any:
25
- pass
23
+ resource_attributes: dict = Field(default_factory=dict)
24
+ headers: dict = Field(default_factory=dict)
26
25
 
27
- def get_meter_context(self) -> Any:
28
- pass
26
+ _exporter: SpanExporter | None = None
27
+ processor: SpanProcessor | None = None
29
28
 
30
29
  def export(self) -> None:
30
+ """Force export of telemetry data."""
31
31
  pass
@@ -12,6 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from typing import Any
16
+
15
17
  from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
16
18
  from opentelemetry.sdk.trace.export import BatchSpanProcessor
17
19
  from pydantic import BaseModel, Field
@@ -41,26 +43,31 @@ class CozeloopExporterConfig(BaseModel):
41
43
  )
42
44
 
43
45
 
44
- class CozeloopExporter(BaseModel, BaseExporter):
46
+ class CozeloopExporter(BaseExporter):
45
47
  config: CozeloopExporterConfig = Field(default_factory=CozeloopExporterConfig)
46
48
 
47
- @override
48
- def get_processor(self):
49
+ def model_post_init(self, context: Any) -> None:
49
50
  headers = {
50
51
  "cozeloop-workspace-id": self.config.space_id,
51
52
  "authorization": f"Bearer {self.config.token}",
52
53
  }
53
- exporter = OTLPSpanExporter(
54
+ self.headers |= headers
55
+
56
+ self._exporter = OTLPSpanExporter(
54
57
  endpoint=self.config.endpoint,
55
- headers=headers,
58
+ headers=self.headers,
56
59
  timeout=10,
57
60
  )
58
- self._real_exporter = exporter
59
- processor = BatchSpanProcessor(exporter)
60
- return processor, None
61
61
 
62
- def export(self):
63
- self._real_exporter.force_flush()
64
- logger.info(
65
- f"CozeloopExporter exports data to {self.config.endpoint}, space id: {self.config.space_id}"
66
- )
62
+ self.processor = BatchSpanProcessor(self._exporter)
63
+
64
+ @override
65
+ def export(self) -> None:
66
+ """Force export of telemetry data."""
67
+ if self._exporter:
68
+ self._exporter.force_flush()
69
+ logger.info(
70
+ f"CozeloopExporter exports data to {self.config.endpoint}, space id: {self.config.space_id}"
71
+ )
72
+ else:
73
+ logger.warning("CozeloopExporter internal exporter is not initialized.")
@@ -12,11 +12,15 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- import typing
16
- from typing import Any
15
+ from typing import Sequence
17
16
 
17
+ from opentelemetry.context import (
18
+ _SUPPRESS_INSTRUMENTATION_KEY,
19
+ attach,
20
+ detach,
21
+ set_value,
22
+ )
18
23
  from opentelemetry.sdk.trace import ReadableSpan, export
19
- from pydantic import BaseModel
20
24
  from typing_extensions import override
21
25
 
22
26
  from veadk.tracing.telemetry.exporters.base_exporter import BaseExporter
@@ -27,20 +31,17 @@ logger = get_logger(__name__)
27
31
 
28
32
  # ======== Adapted from Google ADK ========
29
33
  class _InMemoryExporter(export.SpanExporter):
30
- def __init__(self, session_trace_dict):
34
+ def __init__(self) -> None:
31
35
  super().__init__()
32
36
  self._spans = []
33
- self.session_trace_dict = session_trace_dict
34
37
  self.trace_id = ""
35
- self.prompt_tokens = []
36
- self.completion_tokens = []
38
+ self.session_trace_dict = {}
37
39
 
38
40
  @override
39
- def export(self, spans: typing.Sequence[ReadableSpan]) -> export.SpanExportResult:
41
+ def export(self, spans: Sequence[ReadableSpan]) -> export.SpanExportResult:
40
42
  for span in spans:
41
43
  if span.context:
42
- trace_id = span.context.trace_id
43
- self.trace_id = trace_id
44
+ self.trace_id = span.context.trace_id
44
45
  else:
45
46
  logger.warning(
46
47
  f"Span context is missing, failed to get `trace_id`. span: {span}"
@@ -48,22 +49,12 @@ class _InMemoryExporter(export.SpanExporter):
48
49
 
49
50
  if span.name == "call_llm":
50
51
  attributes = dict(span.attributes or {})
51
- prompt_token = attributes.get("gen_ai.usage.prompt_tokens", None)
52
- completion_token = attributes.get(
53
- "gen_ai.usage.completion_tokens", None
54
- )
55
- if prompt_token:
56
- self.prompt_tokens.append(prompt_token)
57
- if completion_token:
58
- self.completion_tokens.append(completion_token)
59
- if span.name == "call_llm":
60
- attributes = dict(span.attributes or {})
61
- session_id = attributes.get("gcp.vertex.agent.session_id", None)
52
+ session_id = attributes.get("gen_ai.session.id", None)
62
53
  if session_id:
63
54
  if session_id not in self.session_trace_dict:
64
- self.session_trace_dict[session_id] = [trace_id]
55
+ self.session_trace_dict[session_id] = [self.trace_id]
65
56
  else:
66
- self.session_trace_dict[session_id] += [trace_id]
57
+ self.session_trace_dict[session_id] += [self.trace_id]
67
58
  self._spans.extend(spans)
68
59
  return export.SpanExportResult.SUCCESS
69
60
 
@@ -81,14 +72,37 @@ class _InMemoryExporter(export.SpanExporter):
81
72
  self._spans.clear()
82
73
 
83
74
 
84
- class InMemoryExporter(BaseModel, BaseExporter):
85
- name: str = "inmemory_exporter"
75
+ class _InMemorySpanProcessor(export.SimpleSpanProcessor):
76
+ def __init__(self, exporter: _InMemoryExporter) -> None:
77
+ super().__init__(exporter)
78
+ self.spans = []
86
79
 
87
- def model_post_init(self, __context: Any) -> None:
88
- self._exporter = _InMemoryExporter({})
80
+ def on_start(self, span, parent_context) -> None:
81
+ if span.context:
82
+ self.spans.append(span)
89
83
 
90
- @override
91
- def get_processor(self):
92
- self._real_exporter = self._exporter
93
- processor = export.SimpleSpanProcessor(self._exporter)
94
- return processor, None
84
+ def on_end(self, span: ReadableSpan) -> None:
85
+ if span.context:
86
+ if not span.context.trace_flags.sampled:
87
+ return
88
+ token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True))
89
+ try:
90
+ self.span_exporter.export((span,))
91
+ # pylint: disable=broad-exception-caught
92
+ except Exception:
93
+ logger.exception("Exception while exporting Span.")
94
+ detach(token)
95
+ if span in self.spans:
96
+ self.spans.remove(span)
97
+
98
+
99
+ class InMemoryExporter(BaseExporter):
100
+ """InMemory Exporter mainly for store spans in memory for debugging / observability purposes."""
101
+
102
+ def __init__(self, name: str = "inmemory_exporter") -> None:
103
+ super().__init__()
104
+
105
+ self.name = name
106
+
107
+ self._exporter = _InMemoryExporter()
108
+ self.processor = _InMemorySpanProcessor(self._exporter)
@@ -12,6 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from typing import Any
16
+
15
17
  from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
16
18
  from opentelemetry.sdk.trace.export import BatchSpanProcessor
17
19
  from pydantic import BaseModel, Field
@@ -44,28 +46,32 @@ class TLSExporterConfig(BaseModel):
44
46
  secret_key: str = Field(default_factory=lambda: getenv("VOLCENGINE_SECRET_KEY"))
45
47
 
46
48
 
47
- class TLSExporter(BaseModel, BaseExporter):
49
+ class TLSExporter(BaseExporter):
48
50
  config: TLSExporterConfig = Field(default_factory=TLSExporterConfig)
49
51
 
50
- @override
51
- def get_processor(self):
52
+ def model_post_init(self, context: Any) -> None:
52
53
  headers = {
53
54
  "x-tls-otel-tracetopic": self.config.topic_id,
54
55
  "x-tls-otel-ak": self.config.access_key,
55
56
  "x-tls-otel-sk": self.config.secret_key,
56
57
  "x-tls-otel-region": self.config.region,
57
58
  }
58
- exporter = OTLPSpanExporter(
59
+ self.headers |= headers
60
+
61
+ self._exporter = OTLPSpanExporter(
59
62
  endpoint=self.config.endpoint,
60
63
  headers=headers,
61
64
  timeout=10,
62
65
  )
63
- self._real_exporter = exporter
64
- processor = BatchSpanProcessor(exporter)
65
- return processor, None
66
66
 
67
- def export(self):
68
- self._real_exporter.force_flush()
69
- logger.info(
70
- f"TLSExporter exports data to {self.config.endpoint}, topic id: {self.config.topic_id}"
71
- )
67
+ self.processor = BatchSpanProcessor(self._exporter)
68
+
69
+ @override
70
+ def export(self) -> None:
71
+ if self._exporter:
72
+ self._exporter.force_flush()
73
+ logger.info(
74
+ f"TLSExporter exports data to {self.config.endpoint}, topic id: {self.config.topic_id}"
75
+ )
76
+ else:
77
+ logger.warning("TLSExporter internal exporter is not initialized.")