veadk-python 0.2.27__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- veadk/__init__.py +37 -0
- veadk/a2a/__init__.py +13 -0
- veadk/a2a/agent_card.py +45 -0
- veadk/a2a/remote_ve_agent.py +390 -0
- veadk/a2a/utils/__init__.py +13 -0
- veadk/a2a/utils/agent_to_a2a.py +170 -0
- veadk/a2a/ve_a2a_server.py +93 -0
- veadk/a2a/ve_agent_executor.py +78 -0
- veadk/a2a/ve_middlewares.py +313 -0
- veadk/a2a/ve_task_store.py +37 -0
- veadk/agent.py +402 -0
- veadk/agent_builder.py +93 -0
- veadk/agents/loop_agent.py +68 -0
- veadk/agents/parallel_agent.py +72 -0
- veadk/agents/sequential_agent.py +64 -0
- veadk/auth/__init__.py +13 -0
- veadk/auth/base_auth.py +22 -0
- veadk/auth/ve_credential_service.py +203 -0
- veadk/auth/veauth/__init__.py +13 -0
- veadk/auth/veauth/apmplus_veauth.py +58 -0
- veadk/auth/veauth/ark_veauth.py +75 -0
- veadk/auth/veauth/base_veauth.py +50 -0
- veadk/auth/veauth/cozeloop_veauth.py +13 -0
- veadk/auth/veauth/opensearch_veauth.py +75 -0
- veadk/auth/veauth/postgresql_veauth.py +75 -0
- veadk/auth/veauth/prompt_pilot_veauth.py +60 -0
- veadk/auth/veauth/speech_veauth.py +54 -0
- veadk/auth/veauth/utils.py +69 -0
- veadk/auth/veauth/vesearch_veauth.py +62 -0
- veadk/auth/veauth/viking_mem0_veauth.py +91 -0
- veadk/cli/__init__.py +13 -0
- veadk/cli/cli.py +58 -0
- veadk/cli/cli_clean.py +87 -0
- veadk/cli/cli_create.py +163 -0
- veadk/cli/cli_deploy.py +233 -0
- veadk/cli/cli_eval.py +215 -0
- veadk/cli/cli_init.py +214 -0
- veadk/cli/cli_kb.py +110 -0
- veadk/cli/cli_pipeline.py +285 -0
- veadk/cli/cli_prompt.py +86 -0
- veadk/cli/cli_update.py +106 -0
- veadk/cli/cli_uploadevalset.py +139 -0
- veadk/cli/cli_web.py +143 -0
- veadk/cloud/__init__.py +13 -0
- veadk/cloud/cloud_agent_engine.py +485 -0
- veadk/cloud/cloud_app.py +475 -0
- veadk/config.py +115 -0
- veadk/configs/__init__.py +13 -0
- veadk/configs/auth_configs.py +133 -0
- veadk/configs/database_configs.py +132 -0
- veadk/configs/model_configs.py +78 -0
- veadk/configs/tool_configs.py +54 -0
- veadk/configs/tracing_configs.py +110 -0
- veadk/consts.py +74 -0
- veadk/evaluation/__init__.py +17 -0
- veadk/evaluation/adk_evaluator/__init__.py +17 -0
- veadk/evaluation/adk_evaluator/adk_evaluator.py +302 -0
- veadk/evaluation/base_evaluator.py +642 -0
- veadk/evaluation/deepeval_evaluator/__init__.py +17 -0
- veadk/evaluation/deepeval_evaluator/deepeval_evaluator.py +339 -0
- veadk/evaluation/eval_set_file_loader.py +48 -0
- veadk/evaluation/eval_set_recorder.py +146 -0
- veadk/evaluation/types.py +65 -0
- veadk/evaluation/utils/prometheus.py +196 -0
- veadk/integrations/__init__.py +13 -0
- veadk/integrations/ve_apig/__init__.py +13 -0
- veadk/integrations/ve_apig/ve_apig.py +349 -0
- veadk/integrations/ve_apig/ve_apig_utils.py +332 -0
- veadk/integrations/ve_code_pipeline/__init__.py +13 -0
- veadk/integrations/ve_code_pipeline/ve_code_pipeline.py +431 -0
- veadk/integrations/ve_cozeloop/__init__.py +13 -0
- veadk/integrations/ve_cozeloop/ve_cozeloop.py +96 -0
- veadk/integrations/ve_cr/__init__.py +13 -0
- veadk/integrations/ve_cr/ve_cr.py +220 -0
- veadk/integrations/ve_faas/__init__.py +13 -0
- veadk/integrations/ve_faas/template/cookiecutter.json +15 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/__init__.py +13 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/config.yaml.example +6 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/deploy.py +106 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__init__.py +13 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/agent.py +25 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/app.py +202 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/requirements.txt +3 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/run.sh +49 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name }}/__init__.py +14 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name }}/agent.py +27 -0
- veadk/integrations/ve_faas/ve_faas.py +754 -0
- veadk/integrations/ve_faas/ve_faas_utils.py +408 -0
- veadk/integrations/ve_faas/web_template/cookiecutter.json +20 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/__init__.py +13 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/config.yaml.example +2 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/deploy.py +44 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/Dockerfile +23 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/app.py +123 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/init_db.py +46 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/models.py +36 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/requirements.txt +4 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/run.sh +21 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/css/style.css +368 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/js/admin.js +0 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/dashboard.html +21 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/edit_post.html +24 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/login.html +21 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/posts.html +53 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/base.html +45 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/index.html +29 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/post.html +14 -0
- veadk/integrations/ve_identity/__init__.py +110 -0
- veadk/integrations/ve_identity/auth_config.py +261 -0
- veadk/integrations/ve_identity/auth_mixins.py +650 -0
- veadk/integrations/ve_identity/auth_processor.py +385 -0
- veadk/integrations/ve_identity/function_tool.py +158 -0
- veadk/integrations/ve_identity/identity_client.py +864 -0
- veadk/integrations/ve_identity/mcp_tool.py +181 -0
- veadk/integrations/ve_identity/mcp_toolset.py +431 -0
- veadk/integrations/ve_identity/models.py +228 -0
- veadk/integrations/ve_identity/token_manager.py +188 -0
- veadk/integrations/ve_identity/utils.py +151 -0
- veadk/integrations/ve_prompt_pilot/__init__.py +13 -0
- veadk/integrations/ve_prompt_pilot/ve_prompt_pilot.py +85 -0
- veadk/integrations/ve_tls/__init__.py +13 -0
- veadk/integrations/ve_tls/utils.py +116 -0
- veadk/integrations/ve_tls/ve_tls.py +212 -0
- veadk/integrations/ve_tos/ve_tos.py +710 -0
- veadk/integrations/ve_viking_db_memory/__init__.py +13 -0
- veadk/integrations/ve_viking_db_memory/ve_viking_db_memory.py +308 -0
- veadk/knowledgebase/__init__.py +17 -0
- veadk/knowledgebase/backends/__init__.py +13 -0
- veadk/knowledgebase/backends/base_backend.py +72 -0
- veadk/knowledgebase/backends/in_memory_backend.py +91 -0
- veadk/knowledgebase/backends/opensearch_backend.py +162 -0
- veadk/knowledgebase/backends/redis_backend.py +172 -0
- veadk/knowledgebase/backends/utils.py +92 -0
- veadk/knowledgebase/backends/vikingdb_knowledge_backend.py +608 -0
- veadk/knowledgebase/entry.py +25 -0
- veadk/knowledgebase/knowledgebase.py +307 -0
- veadk/memory/__init__.py +35 -0
- veadk/memory/long_term_memory.py +365 -0
- veadk/memory/long_term_memory_backends/__init__.py +13 -0
- veadk/memory/long_term_memory_backends/base_backend.py +35 -0
- veadk/memory/long_term_memory_backends/in_memory_backend.py +67 -0
- veadk/memory/long_term_memory_backends/mem0_backend.py +155 -0
- veadk/memory/long_term_memory_backends/opensearch_backend.py +124 -0
- veadk/memory/long_term_memory_backends/redis_backend.py +140 -0
- veadk/memory/long_term_memory_backends/vikingdb_memory_backend.py +189 -0
- veadk/memory/short_term_memory.py +252 -0
- veadk/memory/short_term_memory_backends/__init__.py +13 -0
- veadk/memory/short_term_memory_backends/base_backend.py +31 -0
- veadk/memory/short_term_memory_backends/mysql_backend.py +49 -0
- veadk/memory/short_term_memory_backends/postgresql_backend.py +49 -0
- veadk/memory/short_term_memory_backends/sqlite_backend.py +55 -0
- veadk/memory/short_term_memory_processor.py +100 -0
- veadk/processors/__init__.py +26 -0
- veadk/processors/base_run_processor.py +120 -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 +150 -0
- veadk/runner.py +732 -0
- veadk/tools/__init__.py +13 -0
- veadk/tools/builtin_tools/__init__.py +13 -0
- veadk/tools/builtin_tools/agent_authorization.py +94 -0
- veadk/tools/builtin_tools/generate_image.py +23 -0
- veadk/tools/builtin_tools/image_edit.py +300 -0
- veadk/tools/builtin_tools/image_generate.py +446 -0
- veadk/tools/builtin_tools/lark.py +67 -0
- veadk/tools/builtin_tools/las.py +24 -0
- veadk/tools/builtin_tools/link_reader.py +66 -0
- veadk/tools/builtin_tools/llm_shield.py +381 -0
- veadk/tools/builtin_tools/load_knowledgebase.py +97 -0
- veadk/tools/builtin_tools/mcp_router.py +29 -0
- veadk/tools/builtin_tools/run_code.py +113 -0
- veadk/tools/builtin_tools/tts.py +253 -0
- veadk/tools/builtin_tools/vesearch.py +49 -0
- veadk/tools/builtin_tools/video_generate.py +363 -0
- veadk/tools/builtin_tools/web_scraper.py +76 -0
- veadk/tools/builtin_tools/web_search.py +83 -0
- veadk/tools/demo_tools.py +58 -0
- veadk/tools/load_knowledgebase_tool.py +149 -0
- veadk/tools/sandbox/__init__.py +13 -0
- veadk/tools/sandbox/browser_sandbox.py +37 -0
- veadk/tools/sandbox/code_sandbox.py +40 -0
- veadk/tools/sandbox/computer_sandbox.py +34 -0
- veadk/tracing/__init__.py +13 -0
- veadk/tracing/base_tracer.py +58 -0
- veadk/tracing/telemetry/__init__.py +13 -0
- veadk/tracing/telemetry/attributes/attributes.py +29 -0
- veadk/tracing/telemetry/attributes/extractors/common_attributes_extractors.py +180 -0
- veadk/tracing/telemetry/attributes/extractors/llm_attributes_extractors.py +858 -0
- veadk/tracing/telemetry/attributes/extractors/tool_attributes_extractors.py +152 -0
- veadk/tracing/telemetry/attributes/extractors/types.py +164 -0
- veadk/tracing/telemetry/exporters/__init__.py +13 -0
- veadk/tracing/telemetry/exporters/apmplus_exporter.py +558 -0
- veadk/tracing/telemetry/exporters/base_exporter.py +39 -0
- veadk/tracing/telemetry/exporters/cozeloop_exporter.py +129 -0
- veadk/tracing/telemetry/exporters/inmemory_exporter.py +248 -0
- veadk/tracing/telemetry/exporters/tls_exporter.py +139 -0
- veadk/tracing/telemetry/opentelemetry_tracer.py +320 -0
- veadk/tracing/telemetry/telemetry.py +411 -0
- veadk/types.py +47 -0
- veadk/utils/__init__.py +13 -0
- veadk/utils/audio_manager.py +95 -0
- veadk/utils/auth.py +294 -0
- veadk/utils/logger.py +59 -0
- veadk/utils/mcp_utils.py +44 -0
- veadk/utils/misc.py +184 -0
- veadk/utils/patches.py +101 -0
- veadk/utils/volcengine_sign.py +205 -0
- veadk/version.py +15 -0
- veadk_python-0.2.27.dist-info/METADATA +373 -0
- veadk_python-0.2.27.dist-info/RECORD +218 -0
- veadk_python-0.2.27.dist-info/WHEEL +5 -0
- veadk_python-0.2.27.dist-info/entry_points.txt +2 -0
- veadk_python-0.2.27.dist-info/licenses/LICENSE +201 -0
- veadk_python-0.2.27.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,411 @@
|
|
|
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.context import get_value
|
|
24
|
+
from opentelemetry.sdk.trace import Span, _Span
|
|
25
|
+
|
|
26
|
+
from veadk.tracing.telemetry.attributes.attributes import ATTRIBUTES
|
|
27
|
+
from veadk.tracing.telemetry.attributes.extractors.types import (
|
|
28
|
+
ExtractorResponse,
|
|
29
|
+
LLMAttributesParams,
|
|
30
|
+
ToolAttributesParams,
|
|
31
|
+
)
|
|
32
|
+
from veadk.utils.logger import get_logger
|
|
33
|
+
from veadk.utils.misc import safe_json_serialize
|
|
34
|
+
|
|
35
|
+
logger = get_logger(__name__)
|
|
36
|
+
|
|
37
|
+
meter_uploader = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _upload_call_llm_metrics(
|
|
41
|
+
invocation_context: InvocationContext,
|
|
42
|
+
event_id: str,
|
|
43
|
+
llm_request: LlmRequest,
|
|
44
|
+
llm_response: LlmResponse,
|
|
45
|
+
) -> None:
|
|
46
|
+
"""Upload LLM call metrics to configured meter uploaders.
|
|
47
|
+
|
|
48
|
+
This function extracts meter uploaders from agent tracers and records
|
|
49
|
+
LLM call metrics including token usage, latency, and request/response details.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
invocation_context: Context containing agent, session, and user information
|
|
53
|
+
event_id: Unique identifier for this LLM call event
|
|
54
|
+
llm_request: The request sent to the language model
|
|
55
|
+
llm_response: The response received from the language model
|
|
56
|
+
"""
|
|
57
|
+
from veadk.agent import Agent
|
|
58
|
+
|
|
59
|
+
if isinstance(invocation_context.agent, Agent):
|
|
60
|
+
tracers = invocation_context.agent.tracers
|
|
61
|
+
for tracer in tracers:
|
|
62
|
+
for exporter in getattr(tracer, "exporters", []):
|
|
63
|
+
if getattr(exporter, "meter_uploader", None):
|
|
64
|
+
global meter_uploader
|
|
65
|
+
meter_uploader = exporter.meter_uploader
|
|
66
|
+
exporter.meter_uploader.record_call_llm(
|
|
67
|
+
invocation_context, event_id, llm_request, llm_response
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _upload_tool_call_metrics(
|
|
72
|
+
tool: BaseTool,
|
|
73
|
+
args: dict[str, Any],
|
|
74
|
+
function_response_event: Event,
|
|
75
|
+
):
|
|
76
|
+
"""Upload tool call metrics to the global meter uploader.
|
|
77
|
+
|
|
78
|
+
Records tool execution metrics including function name, arguments,
|
|
79
|
+
execution time, and response details for observability and debugging.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
tool: The tool instance that was executed
|
|
83
|
+
args: Arguments passed to the tool function
|
|
84
|
+
function_response_event: Event containing the tool's response data
|
|
85
|
+
|
|
86
|
+
Note:
|
|
87
|
+
- Requires global meter_uploader to be initialized
|
|
88
|
+
"""
|
|
89
|
+
global meter_uploader
|
|
90
|
+
if meter_uploader:
|
|
91
|
+
meter_uploader.record_tool_call(tool, args, function_response_event)
|
|
92
|
+
else:
|
|
93
|
+
logger.warning(
|
|
94
|
+
"Meter uploader is not initialized yet. Skip recording tool call metrics."
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _set_agent_input_attribute(
|
|
99
|
+
span: Span, invocation_context: InvocationContext
|
|
100
|
+
) -> None:
|
|
101
|
+
"""Set agent input attributes and events on the given span.
|
|
102
|
+
|
|
103
|
+
This function captures the original user input and adds it as span attributes
|
|
104
|
+
and events in OpenTelemetry format. It handles both text and image content
|
|
105
|
+
while avoiding duplicate entries for the same input.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
span: The OpenTelemetry span to annotate with input data
|
|
109
|
+
invocation_context: Context containing user input and session information
|
|
110
|
+
|
|
111
|
+
Note:
|
|
112
|
+
- Only sets input once per span to avoid duplication
|
|
113
|
+
- Supports multimodal content (text and images)
|
|
114
|
+
- Follows gen_ai attribute conventions
|
|
115
|
+
"""
|
|
116
|
+
event_names = [event.name for event in span.events]
|
|
117
|
+
if "gen_ai.user.message" in event_names:
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
# input = {
|
|
121
|
+
# "agent_name": invocation_context.agent.name,
|
|
122
|
+
# "app_name": invocation_context.session.app_name,
|
|
123
|
+
# "user_id": invocation_context.user_id,
|
|
124
|
+
# "session_id": invocation_context.session.id,
|
|
125
|
+
# "input": invocation_context.user_content.model_dump(exclude_none=True)
|
|
126
|
+
# if invocation_context.user_content
|
|
127
|
+
# else None,
|
|
128
|
+
# }
|
|
129
|
+
|
|
130
|
+
user_content = invocation_context.user_content
|
|
131
|
+
if user_content and user_content.parts:
|
|
132
|
+
# set gen_ai.input attribute required by APMPlus
|
|
133
|
+
span.set_attribute(
|
|
134
|
+
"gen_ai.input",
|
|
135
|
+
safe_json_serialize(user_content.model_dump(exclude_none=True)),
|
|
136
|
+
)
|
|
137
|
+
span.add_event(
|
|
138
|
+
"gen_ai.user.message",
|
|
139
|
+
{
|
|
140
|
+
"agent_name": invocation_context.agent.name,
|
|
141
|
+
"app_name": invocation_context.session.app_name,
|
|
142
|
+
"user_id": invocation_context.user_id,
|
|
143
|
+
"session_id": invocation_context.session.id,
|
|
144
|
+
},
|
|
145
|
+
)
|
|
146
|
+
for idx, part in enumerate(user_content.parts):
|
|
147
|
+
if part.text:
|
|
148
|
+
span.add_event(
|
|
149
|
+
"gen_ai.user.message",
|
|
150
|
+
{f"parts.{idx}.type": "text", f"parts.{idx}.content": part.text},
|
|
151
|
+
)
|
|
152
|
+
if part.inline_data:
|
|
153
|
+
span.add_event(
|
|
154
|
+
"gen_ai.user.message",
|
|
155
|
+
{
|
|
156
|
+
f"parts.{idx}.type": "image_url",
|
|
157
|
+
f"parts.{idx}.image_url.name": (
|
|
158
|
+
part.inline_data.display_name.split("/")[-1]
|
|
159
|
+
if part.inline_data.display_name
|
|
160
|
+
else "<unknown_image_name>"
|
|
161
|
+
),
|
|
162
|
+
f"parts.{idx}.image_url.url": (
|
|
163
|
+
part.inline_data.display_name
|
|
164
|
+
if part.inline_data.display_name
|
|
165
|
+
else "<unknown_image_url>"
|
|
166
|
+
),
|
|
167
|
+
},
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _set_agent_output_attribute(span: Span, llm_response: LlmResponse) -> None:
|
|
172
|
+
"""Set agent output attributes and events on the given span.
|
|
173
|
+
|
|
174
|
+
Captures the LLM response content and adds it as span attributes and events
|
|
175
|
+
in OpenTelemetry format for tracing and observability purposes.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
span: The OpenTelemetry span to annotate with output data
|
|
179
|
+
llm_response: The language model response containing generated content
|
|
180
|
+
|
|
181
|
+
Note:
|
|
182
|
+
- Follows gen_ai attribute conventions
|
|
183
|
+
- Handles multipart responses with proper indexing
|
|
184
|
+
"""
|
|
185
|
+
content = llm_response.content
|
|
186
|
+
if content and content.parts:
|
|
187
|
+
# set gen_ai.output attribute required by APMPlus
|
|
188
|
+
span.set_attribute(
|
|
189
|
+
"gen_ai.output", safe_json_serialize(content.model_dump(exclude_none=True))
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
for idx, part in enumerate(content.parts):
|
|
193
|
+
if part.text:
|
|
194
|
+
span.add_event(
|
|
195
|
+
"gen_ai.choice",
|
|
196
|
+
{
|
|
197
|
+
f"message.parts.{idx}.type": "text",
|
|
198
|
+
f"message.parts.{idx}.text": part.text,
|
|
199
|
+
},
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def set_common_attributes_on_model_span(
|
|
204
|
+
invocation_context: InvocationContext,
|
|
205
|
+
llm_response: LlmResponse,
|
|
206
|
+
current_span: _Span,
|
|
207
|
+
**kwargs,
|
|
208
|
+
) -> None:
|
|
209
|
+
"""Set common attributes on model-related spans including invocation and agent run spans.
|
|
210
|
+
|
|
211
|
+
This function applies standardized attributes across multiple span types to ensure
|
|
212
|
+
consistent telemetry data. It handles token usage accumulation, input/output
|
|
213
|
+
annotation, and hierarchical span attribute propagation.
|
|
214
|
+
|
|
215
|
+
Key Operations:
|
|
216
|
+
- Sets agent input/output on invocation and agent run spans
|
|
217
|
+
- Accumulates token usage across multiple LLM calls
|
|
218
|
+
- Applies common attributes from the ATTRIBUTES mapping
|
|
219
|
+
- Handles span hierarchy and context propagation
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
invocation_context: Context containing agent, session, and user information
|
|
223
|
+
llm_response: The language model response with usage metadata
|
|
224
|
+
current_span: The current OpenTelemetry span being processed
|
|
225
|
+
**kwargs: Additional keyword arguments for attribute extraction
|
|
226
|
+
"""
|
|
227
|
+
common_attributes = ATTRIBUTES.get("common", {})
|
|
228
|
+
try:
|
|
229
|
+
invocation_span: Span = get_value("invocation_span_instance") # type: ignore
|
|
230
|
+
agent_run_span: Span = get_value("agent_run_span_instance") # type: ignore
|
|
231
|
+
|
|
232
|
+
if invocation_span and invocation_span.name.startswith("invocation"):
|
|
233
|
+
_set_agent_input_attribute(invocation_span, invocation_context)
|
|
234
|
+
_set_agent_output_attribute(invocation_span, llm_response)
|
|
235
|
+
for attr_name, attr_extractor in common_attributes.items():
|
|
236
|
+
value = attr_extractor(**kwargs)
|
|
237
|
+
invocation_span.set_attribute(attr_name, value)
|
|
238
|
+
|
|
239
|
+
# Calculate the token usage for the whole invocation span
|
|
240
|
+
current_step_token_usage = (
|
|
241
|
+
llm_response.usage_metadata.total_token_count
|
|
242
|
+
if llm_response.usage_metadata
|
|
243
|
+
and llm_response.usage_metadata.total_token_count
|
|
244
|
+
else 0
|
|
245
|
+
)
|
|
246
|
+
prev_total_token_usage = (
|
|
247
|
+
invocation_span.attributes["gen_ai.usage.total_tokens"]
|
|
248
|
+
if invocation_span.attributes
|
|
249
|
+
else 0
|
|
250
|
+
)
|
|
251
|
+
accumulated_total_token_usage = (
|
|
252
|
+
current_step_token_usage + int(prev_total_token_usage) # type: ignore
|
|
253
|
+
) # we can ignore this warning, cause we manually set the attribute to int before
|
|
254
|
+
invocation_span.set_attribute(
|
|
255
|
+
# record input/output token usage?
|
|
256
|
+
"gen_ai.usage.total_tokens",
|
|
257
|
+
accumulated_total_token_usage,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
if agent_run_span and (
|
|
261
|
+
agent_run_span.name.startswith("agent_run")
|
|
262
|
+
or agent_run_span.name.startswith("invoke_agent")
|
|
263
|
+
):
|
|
264
|
+
_set_agent_input_attribute(agent_run_span, invocation_context)
|
|
265
|
+
_set_agent_output_attribute(agent_run_span, llm_response)
|
|
266
|
+
for attr_name, attr_extractor in common_attributes.items():
|
|
267
|
+
value = attr_extractor(**kwargs)
|
|
268
|
+
agent_run_span.set_attribute(attr_name, value)
|
|
269
|
+
|
|
270
|
+
for attr_name, attr_extractor in common_attributes.items():
|
|
271
|
+
value = attr_extractor(**kwargs)
|
|
272
|
+
current_span.set_attribute(attr_name, value)
|
|
273
|
+
except Exception as e:
|
|
274
|
+
logger.error(f"Failed to set common attributes for spans: {e}")
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def set_common_attributes_on_tool_span(current_span: _Span) -> None:
|
|
278
|
+
"""Set common attributes on tool execution spans.
|
|
279
|
+
|
|
280
|
+
Propagates common attributes from the parent invocation span to tool spans
|
|
281
|
+
to maintain consistent context across the execution trace hierarchy.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
current_span: The tool execution span to annotate with common attributes
|
|
285
|
+
"""
|
|
286
|
+
common_attributes = ATTRIBUTES.get("common", {})
|
|
287
|
+
|
|
288
|
+
invocation_span: Span = get_value("invocation_span_instance") # type: ignore
|
|
289
|
+
|
|
290
|
+
for attr_name in common_attributes.keys():
|
|
291
|
+
if (
|
|
292
|
+
invocation_span
|
|
293
|
+
and invocation_span.name.startswith("invocation")
|
|
294
|
+
and invocation_span.attributes
|
|
295
|
+
and attr_name in invocation_span.attributes
|
|
296
|
+
):
|
|
297
|
+
current_span.set_attribute(attr_name, invocation_span.attributes[attr_name])
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def trace_tool_call(
|
|
301
|
+
tool: BaseTool,
|
|
302
|
+
args: dict[str, Any],
|
|
303
|
+
function_response_event: Event,
|
|
304
|
+
) -> None:
|
|
305
|
+
"""Trace a tool function call with comprehensive telemetry data.
|
|
306
|
+
|
|
307
|
+
This function is the main entry point for tool call tracing, capturing
|
|
308
|
+
execution details, arguments, responses, and performance metrics for
|
|
309
|
+
debugging and observability purposes.
|
|
310
|
+
|
|
311
|
+
Tracing Data Captured:
|
|
312
|
+
- Tool name and function signature
|
|
313
|
+
- Input arguments and parameter values
|
|
314
|
+
- Execution timing and performance metrics
|
|
315
|
+
- Response data and return values
|
|
316
|
+
- Error information if execution fails
|
|
317
|
+
- Common context attributes (user, session, agent)
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
tool: The tool instance being executed
|
|
321
|
+
args: Dictionary of arguments passed to the tool function
|
|
322
|
+
function_response_event: Event containing the tool's execution response
|
|
323
|
+
"""
|
|
324
|
+
span = trace.get_current_span()
|
|
325
|
+
|
|
326
|
+
set_common_attributes_on_tool_span(current_span=span) # type: ignore
|
|
327
|
+
|
|
328
|
+
tool_attributes_mapping = ATTRIBUTES.get("tool", {})
|
|
329
|
+
params = ToolAttributesParams(tool, args, function_response_event)
|
|
330
|
+
|
|
331
|
+
for attr_name, attr_extractor in tool_attributes_mapping.items():
|
|
332
|
+
response: ExtractorResponse = attr_extractor(params)
|
|
333
|
+
ExtractorResponse.update_span(span, attr_name, response)
|
|
334
|
+
|
|
335
|
+
_upload_tool_call_metrics(tool, args, function_response_event)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def trace_call_llm(
|
|
339
|
+
invocation_context: InvocationContext,
|
|
340
|
+
event_id: str,
|
|
341
|
+
llm_request: LlmRequest,
|
|
342
|
+
llm_response: LlmResponse,
|
|
343
|
+
) -> None:
|
|
344
|
+
"""Trace a language model call with comprehensive telemetry data.
|
|
345
|
+
|
|
346
|
+
This function is the main entry point for LLM call tracing, capturing
|
|
347
|
+
request/response details, token usage, timing, and context information
|
|
348
|
+
for cost tracking, performance analysis, and debugging.
|
|
349
|
+
|
|
350
|
+
Tracing Data Captured:
|
|
351
|
+
- Model name and provider information
|
|
352
|
+
- Request parameters and prompt content
|
|
353
|
+
- Response content and metadata
|
|
354
|
+
- Token usage (input, output, total)
|
|
355
|
+
- Execution timing and latency
|
|
356
|
+
- Context information (user, session, agent)
|
|
357
|
+
- Error information if the call fails
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
invocation_context: Context containing agent, session, and user information
|
|
361
|
+
event_id: Unique identifier for this LLM call event
|
|
362
|
+
llm_request: The request object sent to the language model
|
|
363
|
+
llm_response: The response object received from the language model
|
|
364
|
+
"""
|
|
365
|
+
span: Span = trace.get_current_span() # type: ignore
|
|
366
|
+
|
|
367
|
+
from veadk.agent import Agent
|
|
368
|
+
|
|
369
|
+
set_common_attributes_on_model_span(
|
|
370
|
+
invocation_context=invocation_context,
|
|
371
|
+
llm_response=llm_response,
|
|
372
|
+
current_span=span, # type: ignore
|
|
373
|
+
agent_name=invocation_context.agent.name,
|
|
374
|
+
user_id=invocation_context.user_id,
|
|
375
|
+
app_name=invocation_context.app_name,
|
|
376
|
+
session_id=invocation_context.session.id,
|
|
377
|
+
model_provider=invocation_context.agent.model_provider
|
|
378
|
+
if isinstance(invocation_context.agent, Agent)
|
|
379
|
+
else "",
|
|
380
|
+
model_name=invocation_context.agent.model_name
|
|
381
|
+
if isinstance(invocation_context.agent, Agent)
|
|
382
|
+
else "",
|
|
383
|
+
call_type=(
|
|
384
|
+
span.context.trace_state.get("call_type", "")
|
|
385
|
+
if (
|
|
386
|
+
hasattr(span, "context")
|
|
387
|
+
and span.context
|
|
388
|
+
and hasattr(span.context, "trace_state")
|
|
389
|
+
and hasattr(span.context.trace_state, "get")
|
|
390
|
+
)
|
|
391
|
+
else ""
|
|
392
|
+
),
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
llm_attributes_mapping = ATTRIBUTES.get("llm", {})
|
|
396
|
+
params = LLMAttributesParams(
|
|
397
|
+
invocation_context=invocation_context,
|
|
398
|
+
event_id=event_id,
|
|
399
|
+
llm_request=llm_request,
|
|
400
|
+
llm_response=llm_response,
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
for attr_name, attr_extractor in llm_attributes_mapping.items():
|
|
404
|
+
response: ExtractorResponse = attr_extractor(params)
|
|
405
|
+
ExtractorResponse.update_span(span, attr_name, response)
|
|
406
|
+
|
|
407
|
+
_upload_call_llm_metrics(invocation_context, event_id, llm_request, llm_response)
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
# Do not modify this function
|
|
411
|
+
def trace_send_data(): ...
|
veadk/types.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
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, Field
|
|
16
|
+
|
|
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
|
|
21
|
+
from veadk.memory.short_term_memory import ShortTermMemory
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class MediaMessage(BaseModel):
|
|
25
|
+
text: str
|
|
26
|
+
"""Text-based prompt"""
|
|
27
|
+
|
|
28
|
+
media: str
|
|
29
|
+
"""Media file (e.g., `.pdf`, `.docx`, `.png`, `.jpg`, `.jpeg`, `.mp4`, `.mp3`, `.wav`, `.txt`) path"""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AgentRunConfig(BaseModel):
|
|
33
|
+
"""Configuration for running an agent on VeFaaS platform."""
|
|
34
|
+
|
|
35
|
+
model_config = {"arbitrary_types_allowed": True}
|
|
36
|
+
|
|
37
|
+
app_name: str = Field(
|
|
38
|
+
default="veadk_vefaas_app", description="The name of the application"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
agent: Agent | SequentialAgent | ParallelAgent | LoopAgent = Field(
|
|
42
|
+
..., description="The root agent instance"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
short_term_memory: ShortTermMemory = Field(
|
|
46
|
+
default_factory=ShortTermMemory, description="The short-term memory instance"
|
|
47
|
+
)
|
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.
|
|
@@ -0,0 +1,95 @@
|
|
|
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 dataclasses import dataclass
|
|
16
|
+
from typing import Optional
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
import pyaudio
|
|
20
|
+
|
|
21
|
+
PYAUDIO_AVAILABLE = True
|
|
22
|
+
except ImportError:
|
|
23
|
+
pyaudio = None
|
|
24
|
+
PYAUDIO_AVAILABLE = False
|
|
25
|
+
|
|
26
|
+
input_audio_config = {
|
|
27
|
+
"chunk": 3200,
|
|
28
|
+
"format": "pcm",
|
|
29
|
+
"channels": 1,
|
|
30
|
+
"sample_rate": 16000,
|
|
31
|
+
"bit_size": pyaudio.paInt16,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
output_audio_config = {
|
|
35
|
+
"chunk": 3200,
|
|
36
|
+
"format": "pcm",
|
|
37
|
+
"channels": 1,
|
|
38
|
+
"sample_rate": 24000,
|
|
39
|
+
"bit_size": pyaudio.paInt16,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class AudioConfig:
|
|
45
|
+
"""audio config"""
|
|
46
|
+
|
|
47
|
+
format: str
|
|
48
|
+
bit_size: int
|
|
49
|
+
channels: int
|
|
50
|
+
sample_rate: int
|
|
51
|
+
chunk: int
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class AudioDeviceManager:
|
|
55
|
+
"""audio device manager, handle audio input/output"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, input_config: AudioConfig, output_config: AudioConfig):
|
|
58
|
+
if not PYAUDIO_AVAILABLE:
|
|
59
|
+
raise RuntimeError(
|
|
60
|
+
"pyaudio is not installed. Please install it via: "
|
|
61
|
+
"pip install veadk-python[speech]"
|
|
62
|
+
)
|
|
63
|
+
self.input_config = input_config
|
|
64
|
+
self.output_config = output_config
|
|
65
|
+
self.pyaudio = pyaudio.PyAudio()
|
|
66
|
+
self.input_stream: Optional[pyaudio.Stream] = None
|
|
67
|
+
self.output_stream: Optional[pyaudio.Stream] = None
|
|
68
|
+
|
|
69
|
+
def open_input_stream(self) -> pyaudio.Stream:
|
|
70
|
+
# p = pyaudio.PyAudio()
|
|
71
|
+
self.input_stream = self.pyaudio.open(
|
|
72
|
+
format=self.input_config.bit_size,
|
|
73
|
+
channels=self.input_config.channels,
|
|
74
|
+
rate=self.input_config.sample_rate,
|
|
75
|
+
input=True,
|
|
76
|
+
frames_per_buffer=self.input_config.chunk,
|
|
77
|
+
)
|
|
78
|
+
return self.input_stream
|
|
79
|
+
|
|
80
|
+
def open_output_stream(self) -> pyaudio.Stream:
|
|
81
|
+
self.output_stream = self.pyaudio.open(
|
|
82
|
+
format=self.output_config.bit_size,
|
|
83
|
+
channels=self.output_config.channels,
|
|
84
|
+
rate=self.output_config.sample_rate,
|
|
85
|
+
output=True,
|
|
86
|
+
frames_per_buffer=self.output_config.chunk,
|
|
87
|
+
)
|
|
88
|
+
return self.output_stream
|
|
89
|
+
|
|
90
|
+
def cleanup(self) -> None:
|
|
91
|
+
for stream in [self.input_stream, self.output_stream]:
|
|
92
|
+
if stream:
|
|
93
|
+
stream.stop_stream()
|
|
94
|
+
stream.close()
|
|
95
|
+
self.pyaudio.terminate()
|