veadk-python 0.1.0__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/__init__.py +31 -0
- veadk/a2a/__init__.py +13 -0
- veadk/a2a/agent_card.py +45 -0
- veadk/a2a/remote_ve_agent.py +19 -0
- veadk/a2a/ve_a2a_server.py +77 -0
- veadk/a2a/ve_agent_executor.py +78 -0
- veadk/a2a/ve_task_store.py +37 -0
- veadk/agent.py +253 -0
- veadk/cli/__init__.py +13 -0
- veadk/cli/main.py +278 -0
- veadk/cli/services/agentpilot/__init__.py +17 -0
- veadk/cli/services/agentpilot/agentpilot.py +77 -0
- veadk/cli/services/veapig/__init__.py +17 -0
- veadk/cli/services/veapig/apig.py +224 -0
- veadk/cli/services/veapig/apig_utils.py +332 -0
- veadk/cli/services/vefaas/__init__.py +17 -0
- veadk/cli/services/vefaas/template/deploy.py +44 -0
- veadk/cli/services/vefaas/template/src/app.py +30 -0
- veadk/cli/services/vefaas/template/src/config.py +58 -0
- veadk/cli/services/vefaas/vefaas.py +346 -0
- veadk/cli/services/vefaas/vefaas_utils.py +408 -0
- veadk/cli/services/vetls/__init__.py +17 -0
- veadk/cli/services/vetls/vetls.py +87 -0
- veadk/cli/studio/__init__.py +13 -0
- veadk/cli/studio/agent_processor.py +247 -0
- veadk/cli/studio/fast_api.py +232 -0
- veadk/cli/studio/model.py +116 -0
- veadk/cloud/__init__.py +13 -0
- veadk/cloud/cloud_agent_engine.py +144 -0
- veadk/cloud/cloud_app.py +123 -0
- veadk/cloud/template/app.py +30 -0
- veadk/cloud/template/config.py +55 -0
- veadk/config.py +131 -0
- veadk/consts.py +17 -0
- veadk/database/__init__.py +17 -0
- veadk/database/base_database.py +45 -0
- veadk/database/database_factory.py +80 -0
- veadk/database/kv/__init__.py +13 -0
- veadk/database/kv/redis_database.py +109 -0
- veadk/database/local_database.py +43 -0
- veadk/database/relational/__init__.py +13 -0
- veadk/database/relational/mysql_database.py +114 -0
- veadk/database/vector/__init__.py +13 -0
- veadk/database/vector/opensearch_vector_database.py +205 -0
- veadk/database/vector/type.py +50 -0
- veadk/database/viking/__init__.py +13 -0
- veadk/database/viking/viking_database.py +378 -0
- veadk/database/viking/viking_memory_db.py +521 -0
- veadk/evaluation/__init__.py +17 -0
- veadk/evaluation/adk_evaluator/__init__.py +13 -0
- veadk/evaluation/adk_evaluator/adk_evaluator.py +291 -0
- veadk/evaluation/base_evaluator.py +242 -0
- veadk/evaluation/deepeval_evaluator/__init__.py +17 -0
- veadk/evaluation/deepeval_evaluator/deepeval_evaluator.py +223 -0
- veadk/evaluation/eval_set_file_loader.py +28 -0
- veadk/evaluation/eval_set_recorder.py +91 -0
- veadk/evaluation/utils/prometheus.py +142 -0
- veadk/knowledgebase/__init__.py +17 -0
- veadk/knowledgebase/knowledgebase.py +83 -0
- veadk/knowledgebase/knowledgebase_database_adapter.py +259 -0
- veadk/memory/__init__.py +13 -0
- veadk/memory/long_term_memory.py +119 -0
- veadk/memory/memory_database_adapter.py +235 -0
- veadk/memory/short_term_memory.py +124 -0
- veadk/memory/short_term_memory_processor.py +90 -0
- veadk/prompts/__init__.py +13 -0
- veadk/prompts/agent_default_prompt.py +30 -0
- veadk/prompts/prompt_evaluator.py +20 -0
- veadk/prompts/prompt_memory_processor.py +55 -0
- veadk/prompts/prompt_optimization.py +158 -0
- veadk/runner.py +252 -0
- veadk/tools/__init__.py +13 -0
- veadk/tools/builtin_tools/__init__.py +13 -0
- veadk/tools/builtin_tools/lark.py +67 -0
- veadk/tools/builtin_tools/las.py +23 -0
- veadk/tools/builtin_tools/vesearch.py +49 -0
- veadk/tools/builtin_tools/web_scraper.py +76 -0
- veadk/tools/builtin_tools/web_search.py +192 -0
- veadk/tools/demo_tools.py +58 -0
- veadk/tools/load_knowledgebase_tool.py +144 -0
- veadk/tools/sandbox/__init__.py +13 -0
- veadk/tools/sandbox/browser_sandbox.py +27 -0
- veadk/tools/sandbox/code_sandbox.py +30 -0
- veadk/tools/sandbox/computer_sandbox.py +27 -0
- veadk/tracing/__init__.py +13 -0
- veadk/tracing/base_tracer.py +172 -0
- veadk/tracing/telemetry/__init__.py +13 -0
- veadk/tracing/telemetry/exporters/__init__.py +13 -0
- veadk/tracing/telemetry/exporters/apiserver_exporter.py +60 -0
- veadk/tracing/telemetry/exporters/apmplus_exporter.py +101 -0
- veadk/tracing/telemetry/exporters/base_exporter.py +28 -0
- veadk/tracing/telemetry/exporters/cozeloop_exporter.py +69 -0
- veadk/tracing/telemetry/exporters/inmemory_exporter.py +88 -0
- veadk/tracing/telemetry/exporters/tls_exporter.py +78 -0
- veadk/tracing/telemetry/metrics/__init__.py +13 -0
- veadk/tracing/telemetry/metrics/opentelemetry_metrics.py +73 -0
- veadk/tracing/telemetry/opentelemetry_tracer.py +167 -0
- veadk/types.py +23 -0
- veadk/utils/__init__.py +13 -0
- veadk/utils/logger.py +59 -0
- veadk/utils/misc.py +33 -0
- veadk/utils/patches.py +85 -0
- veadk/utils/volcengine_sign.py +199 -0
- veadk/version.py +15 -0
- veadk_python-0.1.0.dist-info/METADATA +124 -0
- veadk_python-0.1.0.dist-info/RECORD +110 -0
- veadk_python-0.1.0.dist-info/WHEEL +5 -0
- veadk_python-0.1.0.dist-info/entry_points.txt +2 -0
- veadk_python-0.1.0.dist-info/licenses/LICENSE +201 -0
- veadk_python-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,28 @@
|
|
|
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 abc import ABC, abstractmethod
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class BaseExporter(ABC):
|
|
20
|
+
def __init__(self) -> None:
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def get_processor(self) -> Any:
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
def get_meter_context(self) -> Any:
|
|
28
|
+
pass
|
|
@@ -0,0 +1,69 @@
|
|
|
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.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
16
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
17
|
+
from pydantic import BaseModel, Field
|
|
18
|
+
from typing_extensions import override
|
|
19
|
+
|
|
20
|
+
from veadk.config import getenv
|
|
21
|
+
from veadk.tracing.telemetry.exporters.base_exporter import BaseExporter
|
|
22
|
+
from veadk.utils.logger import get_logger
|
|
23
|
+
|
|
24
|
+
logger = get_logger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CozeloopExporterConfig(BaseModel):
|
|
28
|
+
endpoint: str = Field(
|
|
29
|
+
...,
|
|
30
|
+
default_factory=lambda: getenv(
|
|
31
|
+
"OBSERVABILITY_OPENTELEMETRY_COZELOOP_ENDPOINT",
|
|
32
|
+
"https://api.coze.cn/v1/loop/opentelemetry/v1/traces",
|
|
33
|
+
),
|
|
34
|
+
)
|
|
35
|
+
space_id: str = Field(
|
|
36
|
+
...,
|
|
37
|
+
default_factory=lambda: getenv(
|
|
38
|
+
"OBSERVABILITY_OPENTELEMETRY_COZELOOP_SERVICE_NAME"
|
|
39
|
+
),
|
|
40
|
+
)
|
|
41
|
+
token: str = Field(
|
|
42
|
+
...,
|
|
43
|
+
default_factory=lambda: getenv("OBSERVABILITY_OPENTELEMETRY_COZELOOP_API_KEY"),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class CozeloopExporter(BaseModel, BaseExporter):
|
|
48
|
+
config: CozeloopExporterConfig = Field(default_factory=CozeloopExporterConfig)
|
|
49
|
+
|
|
50
|
+
@override
|
|
51
|
+
def get_processor(self):
|
|
52
|
+
headers = {
|
|
53
|
+
"cozeloop-workspace-id": self.config.space_id,
|
|
54
|
+
"authorization": f"Bearer {self.config.token}",
|
|
55
|
+
}
|
|
56
|
+
exporter = OTLPSpanExporter(
|
|
57
|
+
endpoint=self.config.endpoint,
|
|
58
|
+
headers=headers,
|
|
59
|
+
timeout=10,
|
|
60
|
+
)
|
|
61
|
+
self._real_exporter = exporter
|
|
62
|
+
processor = BatchSpanProcessor(exporter)
|
|
63
|
+
return processor, None
|
|
64
|
+
|
|
65
|
+
def export(self):
|
|
66
|
+
self._real_exporter.force_flush()
|
|
67
|
+
logger.info(
|
|
68
|
+
f"CozeloopExporter exports data to {self.config.endpoint}, space id: {self.config.space_id}"
|
|
69
|
+
)
|
|
@@ -0,0 +1,88 @@
|
|
|
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 typing
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from opentelemetry.sdk.trace import ReadableSpan, export
|
|
19
|
+
from pydantic import BaseModel
|
|
20
|
+
from typing_extensions import override
|
|
21
|
+
|
|
22
|
+
from veadk.tracing.telemetry.exporters.base_exporter import BaseExporter
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# ======== Adapted from Google ADK ========
|
|
26
|
+
class _InMemoryExporter(export.SpanExporter):
|
|
27
|
+
def __init__(self, session_trace_dict):
|
|
28
|
+
super().__init__()
|
|
29
|
+
self._spans = []
|
|
30
|
+
self.session_trace_dict = session_trace_dict
|
|
31
|
+
self.trace_id = ""
|
|
32
|
+
self.prompt_tokens = []
|
|
33
|
+
self.completion_tokens = []
|
|
34
|
+
|
|
35
|
+
@override
|
|
36
|
+
def export(self, spans: typing.Sequence[ReadableSpan]) -> export.SpanExportResult:
|
|
37
|
+
for span in spans:
|
|
38
|
+
trace_id = span.context.trace_id
|
|
39
|
+
self.trace_id = trace_id
|
|
40
|
+
if span.name == "call_llm":
|
|
41
|
+
attributes = dict(span.attributes)
|
|
42
|
+
prompt_token = attributes.get("gen_ai.usage.prompt_tokens", None)
|
|
43
|
+
completion_token = attributes.get(
|
|
44
|
+
"gen_ai.usage.completion_tokens", None
|
|
45
|
+
)
|
|
46
|
+
if prompt_token:
|
|
47
|
+
self.prompt_tokens.append(prompt_token)
|
|
48
|
+
if completion_token:
|
|
49
|
+
self.completion_tokens.append(completion_token)
|
|
50
|
+
if span.name == "call_llm":
|
|
51
|
+
attributes = dict(span.attributes)
|
|
52
|
+
session_id = attributes.get("gcp.vertex.agent.session_id", None)
|
|
53
|
+
if session_id:
|
|
54
|
+
if session_id not in self.session_trace_dict:
|
|
55
|
+
self.session_trace_dict[session_id] = [trace_id]
|
|
56
|
+
else:
|
|
57
|
+
self.session_trace_dict[session_id] += [trace_id]
|
|
58
|
+
self._spans.extend(spans)
|
|
59
|
+
return export.SpanExportResult.SUCCESS
|
|
60
|
+
|
|
61
|
+
@override
|
|
62
|
+
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
63
|
+
return True
|
|
64
|
+
|
|
65
|
+
def get_finished_spans(self, session_id: str):
|
|
66
|
+
trace_ids = self.session_trace_dict.get(session_id, None)
|
|
67
|
+
if trace_ids is None or not trace_ids:
|
|
68
|
+
return []
|
|
69
|
+
return [x for x in self._spans if x.context.trace_id in trace_ids]
|
|
70
|
+
|
|
71
|
+
def clear(self):
|
|
72
|
+
self._spans.clear()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class InMemoryExporter(BaseModel, BaseExporter):
|
|
76
|
+
name: str = "inmemory_exporter"
|
|
77
|
+
|
|
78
|
+
def model_post_init(self, __context: Any) -> None:
|
|
79
|
+
self._exporter = _InMemoryExporter({})
|
|
80
|
+
|
|
81
|
+
@override
|
|
82
|
+
def get_processor(self):
|
|
83
|
+
self._real_exporter = self._exporter
|
|
84
|
+
processor = export.SimpleSpanProcessor(self._exporter)
|
|
85
|
+
return processor, None
|
|
86
|
+
|
|
87
|
+
def get_meter_context(self) -> Any:
|
|
88
|
+
return None
|
|
@@ -0,0 +1,78 @@
|
|
|
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.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
16
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
17
|
+
from pydantic import BaseModel, Field
|
|
18
|
+
from typing_extensions import override
|
|
19
|
+
|
|
20
|
+
from veadk.config import getenv
|
|
21
|
+
from veadk.tracing.telemetry.exporters.base_exporter import BaseExporter
|
|
22
|
+
from veadk.utils.logger import get_logger
|
|
23
|
+
|
|
24
|
+
logger = get_logger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TLSExporterConfig(BaseModel):
|
|
28
|
+
endpoint: str = Field(
|
|
29
|
+
...,
|
|
30
|
+
default_factory=lambda: getenv(
|
|
31
|
+
"OBSERVABILITY_OPENTELEMETRY_TLS_ENDPOINT",
|
|
32
|
+
"https://tls-cn-beijing.volces.com:4318/v1/traces",
|
|
33
|
+
),
|
|
34
|
+
)
|
|
35
|
+
region: str = Field(
|
|
36
|
+
...,
|
|
37
|
+
default_factory=lambda: getenv(
|
|
38
|
+
"OBSERVABILITY_OPENTELEMETRY_TLS_REGION",
|
|
39
|
+
"cn-beijing",
|
|
40
|
+
),
|
|
41
|
+
)
|
|
42
|
+
topic_id: str = Field(
|
|
43
|
+
...,
|
|
44
|
+
default_factory=lambda: getenv("OBSERVABILITY_OPENTELEMETRY_TLS_SERVICE_NAME"),
|
|
45
|
+
)
|
|
46
|
+
access_key: str = Field(
|
|
47
|
+
..., default_factory=lambda: getenv("VOLCENGINE_ACCESS_KEY")
|
|
48
|
+
)
|
|
49
|
+
secret_key: str = Field(
|
|
50
|
+
..., default_factory=lambda: getenv("VOLCENGINE_SECRET_KEY")
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class TLSExporter(BaseModel, BaseExporter):
|
|
55
|
+
config: TLSExporterConfig = Field(default_factory=TLSExporterConfig)
|
|
56
|
+
|
|
57
|
+
@override
|
|
58
|
+
def get_processor(self):
|
|
59
|
+
headers = {
|
|
60
|
+
"x-tls-otel-tracetopic": self.config.topic_id,
|
|
61
|
+
"x-tls-otel-ak": self.config.access_key,
|
|
62
|
+
"x-tls-otel-sk": self.config.secret_key,
|
|
63
|
+
"x-tls-otel-region": self.config.region,
|
|
64
|
+
}
|
|
65
|
+
exporter = OTLPSpanExporter(
|
|
66
|
+
endpoint=self.config.endpoint,
|
|
67
|
+
headers=headers,
|
|
68
|
+
timeout=10,
|
|
69
|
+
)
|
|
70
|
+
self._real_exporter = exporter
|
|
71
|
+
processor = BatchSpanProcessor(exporter)
|
|
72
|
+
return processor, None
|
|
73
|
+
|
|
74
|
+
def export(self):
|
|
75
|
+
self._real_exporter.force_flush()
|
|
76
|
+
logger.info(
|
|
77
|
+
f"TLSExporter exports data to {self.config.endpoint}, topic id: {self.config.topic_id}"
|
|
78
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
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.
|
|
@@ -0,0 +1,73 @@
|
|
|
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
|
+
|
|
17
|
+
from opentelemetry.metrics._internal import Meter
|
|
18
|
+
from opentelemetry.sdk.metrics import MeterProvider
|
|
19
|
+
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
|
|
20
|
+
|
|
21
|
+
from veadk.config import getenv
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class MeterContext:
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
meter: Meter,
|
|
28
|
+
provider: MeterProvider,
|
|
29
|
+
reader: PeriodicExportingMetricReader,
|
|
30
|
+
):
|
|
31
|
+
self.meter = meter
|
|
32
|
+
self.provider = provider
|
|
33
|
+
self.reader = reader
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class MeterUploader:
|
|
37
|
+
def __init__(self, meter_context: MeterContext):
|
|
38
|
+
self.meter = meter_context.meter
|
|
39
|
+
self.provider = meter_context.provider
|
|
40
|
+
self.reader = meter_context.reader
|
|
41
|
+
|
|
42
|
+
self.base_attributes = {
|
|
43
|
+
"gen_ai_system": "volcengine",
|
|
44
|
+
"server_address": "api.volcengine.com",
|
|
45
|
+
"gen_ai_response_model": getenv("MODEL_AGENT_NAME", "unknown"),
|
|
46
|
+
"stream": "false",
|
|
47
|
+
"gen_ai_operation_name": "chat_completions",
|
|
48
|
+
}
|
|
49
|
+
self.llm_invoke_counter = self.meter.create_counter(
|
|
50
|
+
name="gen_ai.chat.count",
|
|
51
|
+
description="Number of LLM invocations",
|
|
52
|
+
unit="count",
|
|
53
|
+
)
|
|
54
|
+
self.token_usage = self.meter.create_histogram(
|
|
55
|
+
name="gen_ai.client.token.usage",
|
|
56
|
+
description="Token consumption of LLM invocations",
|
|
57
|
+
unit="count",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def record(self, prompt_tokens: list[int], completion_tokens: list[int]):
|
|
61
|
+
self.llm_invoke_counter.add(len(completion_tokens), self.base_attributes)
|
|
62
|
+
|
|
63
|
+
for prompt_token in prompt_tokens:
|
|
64
|
+
token_attributes = {**self.base_attributes, "gen_ai_token_type": "input"}
|
|
65
|
+
self.token_usage.record(prompt_token, attributes=token_attributes)
|
|
66
|
+
for completion_token in completion_tokens:
|
|
67
|
+
token_attributes = {**self.base_attributes, "gen_ai_token_type": "output"}
|
|
68
|
+
self.token_usage.record(completion_token, attributes=token_attributes)
|
|
69
|
+
|
|
70
|
+
def close(self):
|
|
71
|
+
time.sleep(0.05)
|
|
72
|
+
self.reader.force_flush()
|
|
73
|
+
self.provider.shutdown()
|
|
@@ -0,0 +1,167 @@
|
|
|
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 __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import time
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
from openinference.instrumentation.google_adk import GoogleADKInstrumentor
|
|
22
|
+
from opentelemetry.sdk import trace as trace_sdk
|
|
23
|
+
from opentelemetry.sdk.resources import Resource
|
|
24
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
25
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
26
|
+
from typing_extensions import override
|
|
27
|
+
|
|
28
|
+
from veadk.tracing.base_tracer import BaseTracer
|
|
29
|
+
from veadk.tracing.telemetry.exporters.apiserver_exporter import ApiServerExporter
|
|
30
|
+
from veadk.tracing.telemetry.exporters.base_exporter import BaseExporter
|
|
31
|
+
from veadk.tracing.telemetry.exporters.inmemory_exporter import InMemoryExporter
|
|
32
|
+
from veadk.tracing.telemetry.metrics.opentelemetry_metrics import MeterUploader
|
|
33
|
+
from veadk.utils.logger import get_logger
|
|
34
|
+
|
|
35
|
+
logger = get_logger(__name__)
|
|
36
|
+
|
|
37
|
+
DEFAULT_VEADK_TRACER_NAME = "veadk_global_tracer"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def update_resource_attributions(provider: TracerProvider, resource_attributes: dict):
|
|
41
|
+
provider._resource = provider._resource.merge(Resource.create(resource_attributes))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class OpentelemetryTracer(BaseModel, BaseTracer):
|
|
45
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
46
|
+
exporters: list[BaseExporter] = Field(
|
|
47
|
+
default=[],
|
|
48
|
+
description="The exporters to export spans.",
|
|
49
|
+
)
|
|
50
|
+
name: str = Field(
|
|
51
|
+
DEFAULT_VEADK_TRACER_NAME, description="The identifier of tracer."
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def model_post_init(self, context: Any, /) -> None:
|
|
55
|
+
self._processors = []
|
|
56
|
+
self._inmemory_exporter: InMemoryExporter = None
|
|
57
|
+
|
|
58
|
+
# Inmemory & APIServer are the default exporters
|
|
59
|
+
have_inmemory_exporter = False
|
|
60
|
+
have_apiserver_exporter = False
|
|
61
|
+
for exporter in self.exporters:
|
|
62
|
+
if isinstance(exporter, InMemoryExporter):
|
|
63
|
+
have_inmemory_exporter = True
|
|
64
|
+
elif isinstance(exporter, ApiServerExporter):
|
|
65
|
+
have_apiserver_exporter = True
|
|
66
|
+
|
|
67
|
+
if not have_inmemory_exporter:
|
|
68
|
+
inmemory_exporter = InMemoryExporter()
|
|
69
|
+
self.exporters.append(inmemory_exporter)
|
|
70
|
+
self._inmemory_exporter = inmemory_exporter
|
|
71
|
+
if not have_apiserver_exporter:
|
|
72
|
+
apiserver_exporter = ApiServerExporter()
|
|
73
|
+
self.exporters.append(apiserver_exporter)
|
|
74
|
+
self._apiserver_exporter = apiserver_exporter
|
|
75
|
+
|
|
76
|
+
tracer_provider = trace_sdk.TracerProvider()
|
|
77
|
+
for exporter in self.exporters:
|
|
78
|
+
processor, resource_attributes = exporter.get_processor()
|
|
79
|
+
if resource_attributes is not None:
|
|
80
|
+
update_resource_attributions(tracer_provider, resource_attributes)
|
|
81
|
+
tracer_provider.add_span_processor(processor)
|
|
82
|
+
|
|
83
|
+
logger.debug(f"Add exporter `{exporter.__class__.__name__}` to tracing.")
|
|
84
|
+
self._processors.append(processor)
|
|
85
|
+
logger.debug(f"Init OpentelemetryTracer with {len(self.exporters)} exporters.")
|
|
86
|
+
|
|
87
|
+
self._meter_contexts = []
|
|
88
|
+
self._meter_uploaders = []
|
|
89
|
+
for exporter in self.exporters:
|
|
90
|
+
meter_context = exporter.get_meter_context()
|
|
91
|
+
if meter_context is not None:
|
|
92
|
+
self._meter_contexts.append(meter_context)
|
|
93
|
+
|
|
94
|
+
for meter_context in self._meter_contexts:
|
|
95
|
+
meter_uploader = MeterUploader(meter_context)
|
|
96
|
+
self._meter_uploaders.append(meter_uploader)
|
|
97
|
+
|
|
98
|
+
# just for debug
|
|
99
|
+
self._trace_file_path = ""
|
|
100
|
+
|
|
101
|
+
# patch this before starting instrumentation
|
|
102
|
+
# enable_veadk_tracing(self.dump)
|
|
103
|
+
|
|
104
|
+
GoogleADKInstrumentor().instrument(tracer_provider=tracer_provider)
|
|
105
|
+
|
|
106
|
+
@override
|
|
107
|
+
def dump(
|
|
108
|
+
self,
|
|
109
|
+
user_id: str,
|
|
110
|
+
session_id: str,
|
|
111
|
+
path: str = "/tmp",
|
|
112
|
+
) -> str:
|
|
113
|
+
prompt_tokens = self._inmemory_exporter._real_exporter.prompt_tokens
|
|
114
|
+
completion_tokens = self._inmemory_exporter._real_exporter.completion_tokens
|
|
115
|
+
|
|
116
|
+
# upload
|
|
117
|
+
for meter_uploader in self._meter_uploaders:
|
|
118
|
+
meter_uploader.record(
|
|
119
|
+
prompt_tokens=prompt_tokens, completion_tokens=completion_tokens
|
|
120
|
+
)
|
|
121
|
+
# clear tokens after dump
|
|
122
|
+
self._inmemory_exporter._real_exporter.completion_tokens = []
|
|
123
|
+
self._inmemory_exporter._real_exporter.prompt_tokens = []
|
|
124
|
+
|
|
125
|
+
for processor in self._processors:
|
|
126
|
+
time.sleep(0.05) # give some time for the exporter to upload spans
|
|
127
|
+
processor.force_flush()
|
|
128
|
+
|
|
129
|
+
spans = self._inmemory_exporter._real_exporter.get_finished_spans(
|
|
130
|
+
session_id=session_id
|
|
131
|
+
)
|
|
132
|
+
if not spans:
|
|
133
|
+
data = []
|
|
134
|
+
else:
|
|
135
|
+
data = [
|
|
136
|
+
{
|
|
137
|
+
"name": s.name,
|
|
138
|
+
"span_id": s.context.span_id,
|
|
139
|
+
"trace_id": s.context.trace_id,
|
|
140
|
+
"start_time": s.start_time,
|
|
141
|
+
"end_time": s.end_time,
|
|
142
|
+
"attributes": dict(s.attributes),
|
|
143
|
+
"parent_span_id": s.parent.span_id if s.parent else None,
|
|
144
|
+
}
|
|
145
|
+
for s in spans
|
|
146
|
+
]
|
|
147
|
+
|
|
148
|
+
trace_id = hex(int(self._inmemory_exporter._real_exporter.trace_id))[2:]
|
|
149
|
+
self._trace_id = trace_id
|
|
150
|
+
file_path = f"{path}/{self.name}_{user_id}_{session_id}_{trace_id}.json"
|
|
151
|
+
with open(file_path, "w") as f:
|
|
152
|
+
json.dump(data, f, indent=4)
|
|
153
|
+
|
|
154
|
+
self._trace_file_path = file_path
|
|
155
|
+
|
|
156
|
+
for exporter in self.exporters:
|
|
157
|
+
if not isinstance(exporter, InMemoryExporter) and not isinstance(
|
|
158
|
+
exporter, ApiServerExporter
|
|
159
|
+
):
|
|
160
|
+
exporter.export()
|
|
161
|
+
logger.info(
|
|
162
|
+
f"OpenTelemetryTracer tracing done, trace id: {self._trace_id} (hex)"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
self._spans = spans
|
|
166
|
+
logger.info(f"OpenTelemetryTracer dumps {len(spans)} spans to {file_path}")
|
|
167
|
+
return file_path
|
veadk/types.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
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 pydantic import BaseModel
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MediaMessage(BaseModel):
|
|
19
|
+
text: str
|
|
20
|
+
"""Text-based prompt"""
|
|
21
|
+
|
|
22
|
+
media: str
|
|
23
|
+
"""Media file (e.g., `.pdf`, `.docx`, `.png`, `.jpg`, `.jpeg`, `.mp4`, `.mp3`, `.wav`, `.txt`) path"""
|
veadk/utils/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
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.
|
veadk/utils/logger.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
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 sys
|
|
16
|
+
|
|
17
|
+
from loguru import logger
|
|
18
|
+
|
|
19
|
+
from veadk.config import getenv
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def filter_log():
|
|
23
|
+
import logging
|
|
24
|
+
import warnings
|
|
25
|
+
|
|
26
|
+
from urllib3.exceptions import InsecureRequestWarning
|
|
27
|
+
|
|
28
|
+
# ignore all warnings
|
|
29
|
+
warnings.filterwarnings("ignore")
|
|
30
|
+
|
|
31
|
+
# ignore UserWarning
|
|
32
|
+
warnings.filterwarnings(
|
|
33
|
+
"ignore", category=UserWarning, module="opensearchpy.connection.http_urllib3"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# ignore InsecureRequestWarning
|
|
37
|
+
warnings.filterwarnings("ignore", category=InsecureRequestWarning)
|
|
38
|
+
|
|
39
|
+
# disable logs
|
|
40
|
+
logging.basicConfig(level=logging.ERROR)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def setup_logger():
|
|
44
|
+
logger.remove()
|
|
45
|
+
logger.add(
|
|
46
|
+
sys.stdout,
|
|
47
|
+
format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level}</level> | <cyan>{file}:{line}</cyan> - {message}",
|
|
48
|
+
colorize=True,
|
|
49
|
+
level=getenv("LOGGING_LEVEL", "DEBUG"),
|
|
50
|
+
)
|
|
51
|
+
return logger
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
filter_log()
|
|
55
|
+
setup_logger()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_logger(name: str):
|
|
59
|
+
return logger.bind(name=name)
|
veadk/utils/misc.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
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
|
+
|
|
17
|
+
|
|
18
|
+
def read_file(file_path):
|
|
19
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
20
|
+
data = f.readlines()
|
|
21
|
+
data = [x.strip() for x in data]
|
|
22
|
+
return data
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def formatted_timestamp():
|
|
26
|
+
# YYYYMMDDHHMMSS
|
|
27
|
+
return time.strftime("%Y%m%d%H%M%S", time.localtime())
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def read_png_to_bytes(png_path: str) -> bytes:
|
|
31
|
+
with open(png_path, "rb") as f:
|
|
32
|
+
data = f.read()
|
|
33
|
+
return data
|