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.
- veadk/agent.py +3 -13
- veadk/agents/loop_agent.py +55 -0
- veadk/agents/parallel_agent.py +60 -0
- veadk/agents/sequential_agent.py +55 -0
- veadk/cli/cli_deploy.py +11 -0
- veadk/cli/cli_web.py +27 -0
- veadk/evaluation/adk_evaluator/__init__.py +4 -0
- veadk/evaluation/adk_evaluator/adk_evaluator.py +170 -217
- veadk/evaluation/base_evaluator.py +26 -20
- veadk/evaluation/deepeval_evaluator/deepeval_evaluator.py +8 -5
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/app.py +37 -7
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/run.sh +2 -6
- veadk/integrations/ve_faas/ve_faas.py +5 -1
- veadk/runner.py +55 -5
- veadk/tracing/base_tracer.py +25 -200
- veadk/tracing/telemetry/{metrics/__init__.py → attributes/attributes.py} +16 -0
- veadk/tracing/telemetry/attributes/extractors/common_attributes_extractors.py +71 -0
- veadk/tracing/telemetry/attributes/extractors/llm_attributes_extractors.py +392 -0
- veadk/tracing/telemetry/attributes/extractors/tool_attributes_extractors.py +70 -0
- veadk/tracing/telemetry/attributes/extractors/types.py +75 -0
- veadk/tracing/telemetry/exporters/apmplus_exporter.py +97 -38
- veadk/tracing/telemetry/exporters/base_exporter.py +10 -10
- veadk/tracing/telemetry/exporters/cozeloop_exporter.py +20 -13
- veadk/tracing/telemetry/exporters/inmemory_exporter.py +46 -32
- veadk/tracing/telemetry/exporters/tls_exporter.py +18 -12
- veadk/tracing/telemetry/opentelemetry_tracer.py +102 -102
- veadk/tracing/telemetry/telemetry.py +149 -0
- veadk/types.py +6 -1
- veadk/utils/misc.py +1 -1
- veadk/utils/patches.py +25 -0
- veadk/version.py +1 -1
- veadk_python-0.2.4.dist-info/METADATA +345 -0
- veadk_python-0.2.4.dist-info/RECORD +122 -0
- veadk/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/__pycache__/agent.cpython-310.pyc +0 -0
- veadk/__pycache__/config.cpython-310.pyc +0 -0
- veadk/__pycache__/consts.cpython-310.pyc +0 -0
- veadk/__pycache__/runner.cpython-310.pyc +0 -0
- veadk/__pycache__/types.cpython-310.pyc +0 -0
- veadk/__pycache__/version.cpython-310.pyc +0 -0
- veadk/a2a/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/a2a/__pycache__/agent_card.cpython-310.pyc +0 -0
- veadk/a2a/__pycache__/remote_ve_agent.cpython-310.pyc +0 -0
- veadk/a2a/__pycache__/ve_a2a_server.cpython-310.pyc +0 -0
- veadk/a2a/__pycache__/ve_agent_executor.cpython-310.pyc +0 -0
- veadk/cli/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/cli/__pycache__/cli.cpython-310.pyc +0 -0
- veadk/cli/__pycache__/cli_deploy.cpython-310.pyc +0 -0
- veadk/cli/__pycache__/cli_init.cpython-310.pyc +0 -0
- veadk/cli/__pycache__/cli_prompt.cpython-310.pyc +0 -0
- veadk/cli/__pycache__/cli_studio.cpython-310.pyc +0 -0
- veadk/cli/__pycache__/cli_web.cpython-310.pyc +0 -0
- veadk/cli/__pycache__/main.cpython-310.pyc +0 -0
- veadk/cloud/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/cloud/__pycache__/cloud_agent_engine.cpython-310.pyc +0 -0
- veadk/cloud/__pycache__/cloud_app.cpython-310.pyc +0 -0
- veadk/database/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/database/__pycache__/base_database.cpython-310.pyc +0 -0
- veadk/database/__pycache__/database_adapter.cpython-310.pyc +0 -0
- veadk/database/__pycache__/database_factory.cpython-310.pyc +0 -0
- veadk/database/__pycache__/local_database.cpython-310.pyc +0 -0
- veadk/database/kv/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/database/relational/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/database/vector/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/database/vector/__pycache__/opensearch_vector_database.cpython-310.pyc +0 -0
- veadk/database/vector/__pycache__/type.cpython-310.pyc +0 -0
- veadk/database/viking/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/evaluation/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/evaluation/__pycache__/base_evaluator.cpython-310.pyc +0 -0
- veadk/evaluation/__pycache__/eval_set_file_loader.cpython-310.pyc +0 -0
- veadk/evaluation/__pycache__/eval_set_recorder.cpython-310.pyc +0 -0
- veadk/evaluation/__pycache__/types.cpython-310.pyc +0 -0
- veadk/evaluation/adk_evaluator/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/evaluation/deepeval_evaluator/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/evaluation/deepeval_evaluator/__pycache__/deepeval_evaluator.cpython-310.pyc +0 -0
- veadk/evaluation/utils/__pycache__/prometheus.cpython-310.pyc +0 -0
- veadk/integrations/ve_apig/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/integrations/ve_apig/__pycache__/apig.cpython-310.pyc +0 -0
- veadk/integrations/ve_apig/__pycache__/ve_apig.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/__pycache__/types.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/__pycache__/ve_faas.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/__pycache__/ve_faas_utils.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/__pycache__/vefaas.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/__pycache__/vefaas_utils.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__pycache__/agent.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__pycache__/app.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__pycache__/studio_app.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name|replace('-', '_') }}/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name|replace('-', '_') }}/__pycache__/agent.cpython-310.pyc +0 -0
- veadk/integrations/ve_prompt_pilot/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/integrations/ve_prompt_pilot/__pycache__/agentpilot.cpython-310.pyc +0 -0
- veadk/knowledgebase/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/knowledgebase/__pycache__/knowledgebase.cpython-310.pyc +0 -0
- veadk/knowledgebase/__pycache__/knowledgebase_database_adapter.cpython-310.pyc +0 -0
- veadk/memory/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/memory/__pycache__/long_term_memory.cpython-310.pyc +0 -0
- veadk/memory/__pycache__/memory_database_adapter.cpython-310.pyc +0 -0
- veadk/memory/__pycache__/short_term_memory.cpython-310.pyc +0 -0
- veadk/memory/__pycache__/short_term_memory_processor.cpython-310.pyc +0 -0
- veadk/prompts/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/prompts/__pycache__/agent_default_prompt.cpython-310.pyc +0 -0
- veadk/prompts/__pycache__/prompt_memory_processor.cpython-310.pyc +0 -0
- veadk/prompts/__pycache__/prompt_optimization.cpython-310.pyc +0 -0
- veadk/tools/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/tools/__pycache__/demo_tools.cpython-310.pyc +0 -0
- veadk/tools/__pycache__/load_knowledgebase_tool.cpython-310.pyc +0 -0
- veadk/tools/builtin_tools/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/tools/builtin_tools/__pycache__/lark.cpython-310.pyc +0 -0
- veadk/tools/builtin_tools/__pycache__/vesearch.cpython-310.pyc +0 -0
- veadk/tools/builtin_tools/__pycache__/web_search.cpython-310.pyc +0 -0
- veadk/tools/sandbox/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/tracing/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/tracing/__pycache__/base_tracer.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/__pycache__/opentelemetry_tracer.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/exporters/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/exporters/__pycache__/apiserver_exporter.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/exporters/__pycache__/apmplus_exporter.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/exporters/__pycache__/base_exporter.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/exporters/__pycache__/cozeloop_exporter.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/exporters/__pycache__/inmemory_exporter.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/exporters/__pycache__/tls_exporter.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/metrics/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/metrics/__pycache__/opentelemetry_metrics.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/metrics/opentelemetry_metrics.py +0 -73
- veadk/utils/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/utils/__pycache__/logger.cpython-310.pyc +0 -0
- veadk/utils/__pycache__/mcp_utils.cpython-310.pyc +0 -0
- veadk/utils/__pycache__/misc.cpython-310.pyc +0 -0
- veadk/utils/__pycache__/patches.cpython-310.pyc +0 -0
- veadk/utils/__pycache__/volcengine_sign.cpython-310.pyc +0 -0
- veadk_python-0.2.2.dist-info/METADATA +0 -144
- veadk_python-0.2.2.dist-info/RECORD +0 -213
- {veadk_python-0.2.2.dist-info → veadk_python-0.2.4.dist-info}/WHEEL +0 -0
- {veadk_python-0.2.2.dist-info → veadk_python-0.2.4.dist-info}/entry_points.txt +0 -0
- {veadk_python-0.2.2.dist-info → veadk_python-0.2.4.dist-info}/licenses/LICENSE +0 -0
- {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.
|
|
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(
|
|
122
|
+
class APMPlusExporter(BaseExporter):
|
|
53
123
|
config: APMPlusExporterConfig = Field(default_factory=APMPlusExporterConfig)
|
|
54
124
|
|
|
55
|
-
|
|
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
|
-
|
|
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
|
-
|
|
83
|
-
headers = {
|
|
84
|
-
"x-byteapm-appkey": self.config.app_key,
|
|
85
|
-
}
|
|
134
|
+
self.resource_attributes |= resource_attributes
|
|
86
135
|
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
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
|
|
16
|
-
from
|
|
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(
|
|
20
|
-
|
|
21
|
-
pass
|
|
20
|
+
class BaseExporter(BaseModel):
|
|
21
|
+
model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow")
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
pass
|
|
23
|
+
resource_attributes: dict = Field(default_factory=dict)
|
|
24
|
+
headers: dict = Field(default_factory=dict)
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
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(
|
|
46
|
+
class CozeloopExporter(BaseExporter):
|
|
45
47
|
config: CozeloopExporterConfig = Field(default_factory=CozeloopExporterConfig)
|
|
46
48
|
|
|
47
|
-
|
|
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
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
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
|
|
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.
|
|
36
|
-
self.completion_tokens = []
|
|
38
|
+
self.session_trace_dict = {}
|
|
37
39
|
|
|
38
40
|
@override
|
|
39
|
-
def export(self, spans:
|
|
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
|
-
|
|
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
|
|
85
|
-
|
|
75
|
+
class _InMemorySpanProcessor(export.SimpleSpanProcessor):
|
|
76
|
+
def __init__(self, exporter: _InMemoryExporter) -> None:
|
|
77
|
+
super().__init__(exporter)
|
|
78
|
+
self.spans = []
|
|
86
79
|
|
|
87
|
-
def
|
|
88
|
-
|
|
80
|
+
def on_start(self, span, parent_context) -> None:
|
|
81
|
+
if span.context:
|
|
82
|
+
self.spans.append(span)
|
|
89
83
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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(
|
|
49
|
+
class TLSExporter(BaseExporter):
|
|
48
50
|
config: TLSExporterConfig = Field(default_factory=TLSExporterConfig)
|
|
49
51
|
|
|
50
|
-
|
|
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
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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.")
|