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,253 @@
|
|
|
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 os
|
|
16
|
+
import requests
|
|
17
|
+
import json
|
|
18
|
+
import base64
|
|
19
|
+
import time
|
|
20
|
+
import queue
|
|
21
|
+
import threading
|
|
22
|
+
import tempfile
|
|
23
|
+
from typing import Dict, Any
|
|
24
|
+
from google.adk.tools import ToolContext
|
|
25
|
+
from veadk.config import getenv, settings
|
|
26
|
+
from veadk.utils.logger import get_logger
|
|
27
|
+
|
|
28
|
+
logger = get_logger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def text_to_speech(text: str, tool_context: ToolContext) -> Dict[str, Any]:
|
|
32
|
+
"""TTS provides users with the ability to convert text to speech, turning the text content of LLM into audio.
|
|
33
|
+
Use this tool when you need to convert text content into audible speech.
|
|
34
|
+
It transforms plain text into natural-sounding speech, as well as exporting the generated audio in pcm format.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
text: The text to convert.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
A dict with the saved audio path.
|
|
41
|
+
"""
|
|
42
|
+
url = "https://openspeech.bytedance.com/api/v3/tts/unidirectional"
|
|
43
|
+
temp_dir = getenv("TOOL_VESPEECH_AUDIO_OUTPUT_PATH", tempfile.gettempdir())
|
|
44
|
+
|
|
45
|
+
app_id = getenv("TOOL_VESPEECH_APP_ID")
|
|
46
|
+
speaker = getenv(
|
|
47
|
+
"TOOL_VESPEECH_SPEAKER", "zh_female_vv_uranus_bigtts"
|
|
48
|
+
) # e.g. zh_female_vv_mars_bigtts
|
|
49
|
+
api_key = settings.tool.vespeech.api_key
|
|
50
|
+
if not all([app_id, api_key, speaker]):
|
|
51
|
+
return {
|
|
52
|
+
"error": (
|
|
53
|
+
"Tool text_to_speech execution failed. Missing required env vars: "
|
|
54
|
+
"TOOL_VESPEECH_APP_ID, TOOL_VESPEECH_API_KEY, TOOL_VESPEECH_SPEAKER"
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
headers = {
|
|
59
|
+
"X-Api-App-Id": app_id,
|
|
60
|
+
"X-Api-Key": api_key,
|
|
61
|
+
"X-Api-Resource-Id": "seed-tts-2.0", # seed-tts-1.0 or seed-tts-2.0
|
|
62
|
+
"Content-Type": "application/json",
|
|
63
|
+
"Connection": "keep-alive",
|
|
64
|
+
}
|
|
65
|
+
additions = {
|
|
66
|
+
"explicit_language": "zh",
|
|
67
|
+
"disable_markdown_filter": True,
|
|
68
|
+
"enable_timestamp": True,
|
|
69
|
+
}
|
|
70
|
+
payload = {
|
|
71
|
+
"user": {"uid": tool_context._invocation_context.user_id},
|
|
72
|
+
"req_params": {
|
|
73
|
+
"text": text,
|
|
74
|
+
"speaker": speaker,
|
|
75
|
+
"audio_params": {
|
|
76
|
+
"format": "pcm",
|
|
77
|
+
"bit_rate": 16000,
|
|
78
|
+
"sample_rate": 24000,
|
|
79
|
+
"enable_timestamp": True,
|
|
80
|
+
},
|
|
81
|
+
"additions": json.dumps(additions),
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
session = requests.Session()
|
|
86
|
+
response = None
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
logger.debug(f"Request TTS server with payload: {payload}.")
|
|
90
|
+
response = session.post(url, headers=headers, json=payload, stream=True)
|
|
91
|
+
|
|
92
|
+
os.makedirs(temp_dir, exist_ok=True)
|
|
93
|
+
with tempfile.NamedTemporaryFile(
|
|
94
|
+
suffix=".pcm", delete=False, dir=temp_dir
|
|
95
|
+
) as tmp:
|
|
96
|
+
audio_save_path = tmp.name # e.g. /tmp/tts_12345.pcm
|
|
97
|
+
logger.debug(f"Created temporary file: {audio_save_path}")
|
|
98
|
+
|
|
99
|
+
handle_server_response(response, audio_save_path)
|
|
100
|
+
|
|
101
|
+
except Exception as e:
|
|
102
|
+
logger.error(
|
|
103
|
+
f"Failed to convert text to speech: {e}Response content: {response}"
|
|
104
|
+
)
|
|
105
|
+
return {
|
|
106
|
+
"error": f"Tool text_to_speech execution failed. "
|
|
107
|
+
f"Response content: {response}"
|
|
108
|
+
f"Execution Error: {e}"
|
|
109
|
+
}
|
|
110
|
+
finally:
|
|
111
|
+
if response:
|
|
112
|
+
response.close()
|
|
113
|
+
session.close()
|
|
114
|
+
logger.debug("Finish convert text to speech")
|
|
115
|
+
return {"saved_audio_path": audio_save_path}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def handle_server_response(
|
|
119
|
+
response: requests.models.Response, audio_save_path: str
|
|
120
|
+
) -> None:
|
|
121
|
+
"""
|
|
122
|
+
Handle the server response for TTS.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
response: The server response as a requests.models.Response object.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
None
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
# audio data buffer
|
|
132
|
+
audio_data = bytearray()
|
|
133
|
+
# audio data queue for player thread
|
|
134
|
+
audio_queue = queue.Queue()
|
|
135
|
+
total_audio_size = 0
|
|
136
|
+
|
|
137
|
+
output_stream, player_thread = None, None
|
|
138
|
+
stop_event = threading.Event()
|
|
139
|
+
try:
|
|
140
|
+
from veadk.utils.audio_manager import (
|
|
141
|
+
AudioDeviceManager,
|
|
142
|
+
AudioConfig,
|
|
143
|
+
input_audio_config,
|
|
144
|
+
output_audio_config,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
audio_device = AudioDeviceManager(
|
|
148
|
+
AudioConfig(**input_audio_config), AudioConfig(**output_audio_config)
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# init output stream
|
|
152
|
+
output_stream = audio_device.open_output_stream()
|
|
153
|
+
player_thread = threading.Thread(
|
|
154
|
+
target=_audio_player_thread, args=(audio_queue, output_stream, stop_event)
|
|
155
|
+
)
|
|
156
|
+
player_thread.daemon = True
|
|
157
|
+
player_thread.start()
|
|
158
|
+
except Exception as e:
|
|
159
|
+
logger.error(f"Failed to initialize audio device: {e}")
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
for chunk in response.iter_lines(decode_unicode=True):
|
|
163
|
+
if not chunk:
|
|
164
|
+
continue
|
|
165
|
+
data = json.loads(chunk)
|
|
166
|
+
|
|
167
|
+
if data.get("code", 0) == 0 and "data" in data and data["data"]:
|
|
168
|
+
chunk_audio = base64.b64decode(data["data"])
|
|
169
|
+
audio_size = len(chunk_audio)
|
|
170
|
+
total_audio_size += audio_size
|
|
171
|
+
audio_queue.put(chunk_audio)
|
|
172
|
+
audio_data.extend(chunk_audio)
|
|
173
|
+
continue
|
|
174
|
+
if data.get("code", 0) == 0 and "sentence" in data and data["sentence"]:
|
|
175
|
+
logger.debug(f"sentence_data: {data}")
|
|
176
|
+
continue
|
|
177
|
+
if data.get("code", 0) == 20000000:
|
|
178
|
+
logger.debug(
|
|
179
|
+
f"successfully get audio data, total size: {total_audio_size / 1024:.2f} KB"
|
|
180
|
+
)
|
|
181
|
+
break
|
|
182
|
+
if data.get("code", 0) > 0:
|
|
183
|
+
logger.debug(f"tts response error:{data}")
|
|
184
|
+
break
|
|
185
|
+
|
|
186
|
+
# save audio data to file
|
|
187
|
+
save_output_to_file(audio_data, audio_save_path)
|
|
188
|
+
except Exception as e:
|
|
189
|
+
logger.error(f"handle tts failed: {e}, response: {response}")
|
|
190
|
+
raise
|
|
191
|
+
finally:
|
|
192
|
+
if output_stream:
|
|
193
|
+
audio_queue.join()
|
|
194
|
+
stop_event.set()
|
|
195
|
+
if player_thread and player_thread.is_alive():
|
|
196
|
+
player_thread.join()
|
|
197
|
+
output_stream.close()
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _audio_player_thread(audio_queue, output_stream, stop_event):
|
|
201
|
+
"""
|
|
202
|
+
Play audio data from queue.
|
|
203
|
+
Args:
|
|
204
|
+
audio_queue: The queue to store audio data.
|
|
205
|
+
output_stream: The output stream to play audio.
|
|
206
|
+
stop_event: The event to stop the thread.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
|
|
210
|
+
"""
|
|
211
|
+
while not stop_event.is_set():
|
|
212
|
+
try:
|
|
213
|
+
# write audio data to output stream
|
|
214
|
+
audio_data = audio_queue.get(timeout=1.0)
|
|
215
|
+
if audio_data:
|
|
216
|
+
output_stream.write(audio_data)
|
|
217
|
+
audio_queue.task_done()
|
|
218
|
+
except queue.Empty:
|
|
219
|
+
# if queue is empty, sleep for a while
|
|
220
|
+
time.sleep(0.1)
|
|
221
|
+
except Exception as e:
|
|
222
|
+
logger.error(f"Failed to play audio data: {e}")
|
|
223
|
+
time.sleep(0.1)
|
|
224
|
+
logger.debug("audio player thread exited")
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def save_output_to_file(audio_data: bytearray, filename: str) -> None:
|
|
228
|
+
"""
|
|
229
|
+
Save audio data to file.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
audio_data: The audio data as bytes.
|
|
233
|
+
filename: The filename to save the audio data.
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
None
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
if not audio_data:
|
|
240
|
+
logger.debug("No audio data to save.")
|
|
241
|
+
return
|
|
242
|
+
if not filename:
|
|
243
|
+
logger.debug("No filename to save audio data.")
|
|
244
|
+
return
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
with open(filename, "wb") as f:
|
|
248
|
+
f.write(audio_data)
|
|
249
|
+
logger.debug(
|
|
250
|
+
f"Successfully save audio file to {filename},file size: {len(audio_data) / 1024:.2f} KB"
|
|
251
|
+
)
|
|
252
|
+
except IOError as e:
|
|
253
|
+
logger.error(f"Failed to save pcm file: {e}")
|
|
@@ -0,0 +1,49 @@
|
|
|
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 requests
|
|
16
|
+
|
|
17
|
+
from veadk.config import getenv, settings
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def vesearch(query: str) -> str:
|
|
21
|
+
"""Search information from Internet, social media, news sites, etc.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
query: The query string to search.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
Summarized search results.
|
|
28
|
+
"""
|
|
29
|
+
api_key = settings.tool.vesearch.api_key
|
|
30
|
+
bot_id = str(getenv("TOOL_VESEARCH_ENDPOINT"))
|
|
31
|
+
|
|
32
|
+
if api_key == "":
|
|
33
|
+
return "Invoke `vesearch` failed. Please set TOOL_VESEARCH_API_KEY as your environment variable."
|
|
34
|
+
if bot_id == "":
|
|
35
|
+
return "Invoke `vesearch` failed. Please set TOOL_VESEARCH_ENDPOINT as your environment variable."
|
|
36
|
+
|
|
37
|
+
URL = "https://open.feedcoopapi.com/agent_api/agent/chat/completion"
|
|
38
|
+
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
|
|
39
|
+
data = {
|
|
40
|
+
"bot_id": bot_id,
|
|
41
|
+
"messages": [{"role": "user", "content": query}],
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
response = requests.post(URL, json=data, headers=headers)
|
|
45
|
+
if response.status_code == 200:
|
|
46
|
+
result = response.json()
|
|
47
|
+
return result["choices"][0]["message"]["content"]
|
|
48
|
+
else:
|
|
49
|
+
return response.text
|
|
@@ -0,0 +1,363 @@
|
|
|
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 json
|
|
16
|
+
import time
|
|
17
|
+
import traceback
|
|
18
|
+
from typing import Dict, cast
|
|
19
|
+
|
|
20
|
+
from google.adk.tools import ToolContext
|
|
21
|
+
from opentelemetry import trace
|
|
22
|
+
from opentelemetry.trace import Span
|
|
23
|
+
from volcenginesdkarkruntime import Ark
|
|
24
|
+
from volcenginesdkarkruntime.types.content_generation.create_task_content_param import (
|
|
25
|
+
CreateTaskContentParam,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
from veadk.config import getenv, settings
|
|
29
|
+
from veadk.consts import DEFAULT_VIDEO_MODEL_API_BASE, DEFAULT_VIDEO_MODEL_NAME
|
|
30
|
+
from veadk.utils.logger import get_logger
|
|
31
|
+
from veadk.version import VERSION
|
|
32
|
+
|
|
33
|
+
logger = get_logger(__name__)
|
|
34
|
+
|
|
35
|
+
client = Ark(
|
|
36
|
+
api_key=getenv(
|
|
37
|
+
"MODEL_VIDEO_API_KEY", getenv("MODEL_AGENT_API_KEY", settings.model.api_key)
|
|
38
|
+
),
|
|
39
|
+
base_url=getenv("MODEL_VIDEO_API_BASE", DEFAULT_VIDEO_MODEL_API_BASE),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
async def generate(prompt, first_frame_image=None, last_frame_image=None):
|
|
44
|
+
try:
|
|
45
|
+
if first_frame_image is None:
|
|
46
|
+
response = client.content_generation.tasks.create(
|
|
47
|
+
model=getenv("MODEL_VIDEO_NAME", DEFAULT_VIDEO_MODEL_NAME),
|
|
48
|
+
content=[
|
|
49
|
+
{"type": "text", "text": prompt},
|
|
50
|
+
],
|
|
51
|
+
)
|
|
52
|
+
elif last_frame_image is None:
|
|
53
|
+
response = client.content_generation.tasks.create(
|
|
54
|
+
model=getenv("MODEL_VIDEO_NAME", DEFAULT_VIDEO_MODEL_NAME),
|
|
55
|
+
content=cast(
|
|
56
|
+
list[CreateTaskContentParam], # avoid IDE warning
|
|
57
|
+
[
|
|
58
|
+
{"type": "text", "text": prompt},
|
|
59
|
+
{
|
|
60
|
+
"type": "image_url",
|
|
61
|
+
"image_url": {"url": first_frame_image},
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
),
|
|
65
|
+
)
|
|
66
|
+
else:
|
|
67
|
+
response = client.content_generation.tasks.create(
|
|
68
|
+
model=getenv("MODEL_VIDEO_NAME", DEFAULT_VIDEO_MODEL_NAME),
|
|
69
|
+
content=[
|
|
70
|
+
{"type": "text", "text": prompt},
|
|
71
|
+
{
|
|
72
|
+
"type": "image_url",
|
|
73
|
+
"image_url": {"url": first_frame_image},
|
|
74
|
+
"role": "first_frame",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"type": "image_url",
|
|
78
|
+
"image_url": {"url": last_frame_image},
|
|
79
|
+
"role": "last_frame",
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
)
|
|
83
|
+
except:
|
|
84
|
+
traceback.print_exc()
|
|
85
|
+
raise
|
|
86
|
+
return response
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
async def video_generate(
|
|
90
|
+
params: list, tool_context: ToolContext, batch_size: int = 10
|
|
91
|
+
) -> Dict:
|
|
92
|
+
"""
|
|
93
|
+
Generate videos in **batch** from text prompts, optionally guided by a first/last frame,
|
|
94
|
+
and fine-tuned via *model text commands* (a.k.a. `parameters` appended to the prompt).
|
|
95
|
+
|
|
96
|
+
This API creates video-generation tasks. Each item in `params` describes a single video.
|
|
97
|
+
The function submits all items in one call and returns task metadata for tracking.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
params (list[dict]):
|
|
101
|
+
A list of video generation requests. Each item supports the fields below.
|
|
102
|
+
batch_size (int):
|
|
103
|
+
The number of videos to generate in a batch. Defaults to 10.
|
|
104
|
+
|
|
105
|
+
Required per item:
|
|
106
|
+
- video_name (str):
|
|
107
|
+
Name/identifier of the output video file.
|
|
108
|
+
|
|
109
|
+
- prompt (str):
|
|
110
|
+
Text describing the video to generate. Supports zh/EN.
|
|
111
|
+
You may append **model text commands** after the prompt to control resolution,
|
|
112
|
+
aspect ratio, duration, fps, watermark, seed, camera lock, etc.
|
|
113
|
+
Format: `... --rs <resolution> --rt <ratio> --dur <seconds> --fps <fps> --wm <bool> --seed <int> --cf <bool>`
|
|
114
|
+
Example:
|
|
115
|
+
"小猫骑着滑板穿过公园。 --rs 720p --rt 16:9 --dur 5 --fps 24 --wm true --seed 11 --cf false"
|
|
116
|
+
|
|
117
|
+
Optional per item:
|
|
118
|
+
- first_frame (str | None):
|
|
119
|
+
URL or Base64 string (data URL) for the **first frame** (role = `first_frame`).
|
|
120
|
+
Use when you want the clip to start from a specific image.
|
|
121
|
+
|
|
122
|
+
- last_frame (str | None):
|
|
123
|
+
URL or Base64 string (data URL) for the **last frame** (role = `last_frame`).
|
|
124
|
+
Use when you want the clip to end on a specific image.
|
|
125
|
+
|
|
126
|
+
Notes on first/last frame:
|
|
127
|
+
* When both frames are provided, **match width/height** to avoid cropping; if they differ,
|
|
128
|
+
the tail frame may be auto-cropped to fit.
|
|
129
|
+
* If you only need one guided frame, provide either `first_frame` or `last_frame` (not both).
|
|
130
|
+
|
|
131
|
+
Image input constraints (for first/last frame):
|
|
132
|
+
- Formats: jpeg, png, webp, bmp, tiff, gif
|
|
133
|
+
- Aspect ratio (宽:高): 0.4–2.5
|
|
134
|
+
- Width/Height (px): 300–6000
|
|
135
|
+
- Size: < 30 MB
|
|
136
|
+
- Base64 data URL example: `data:image/png;base64,<BASE64>`
|
|
137
|
+
|
|
138
|
+
Model text commands (append after the prompt; unsupported keys are ignored by some models):
|
|
139
|
+
--rs / --resolution <value> Video resolution. Common values: 480p, 720p, 1080p.
|
|
140
|
+
Default depends on model (e.g., doubao-seedance-1-0-pro: 1080p,
|
|
141
|
+
some others default 720p).
|
|
142
|
+
|
|
143
|
+
--rt / --ratio <value> Aspect ratio. Typical: 16:9 (default), 9:16, 4:3, 3:4, 1:1, 2:1, 21:9.
|
|
144
|
+
Some models support `keep_ratio` (keep source image ratio) or `adaptive`
|
|
145
|
+
(auto choose suitable ratio).
|
|
146
|
+
|
|
147
|
+
--dur / --duration <seconds> Clip length in seconds. Seedance supports **3–12 s**;
|
|
148
|
+
Wan2.1 仅支持 5 s。Default varies by model.
|
|
149
|
+
|
|
150
|
+
--fps / --framespersecond <int> Frame rate. Common: 16 or 24 (model-dependent; e.g., seaweed=24, wan2.1=16).
|
|
151
|
+
|
|
152
|
+
--wm / --watermark <true|false> Whether to add watermark. Default: **false** (per doc).
|
|
153
|
+
|
|
154
|
+
--seed <int> Random seed in [-1, 2^32-1]. Default **-1** = auto seed.
|
|
155
|
+
Same seed may yield similar (not guaranteed identical) results across runs.
|
|
156
|
+
|
|
157
|
+
--cf / --camerafixed <true|false> Lock camera movement. Some models support this flag.
|
|
158
|
+
true: try to keep camera fixed; false: allow movement. Default: **false**.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Dict:
|
|
162
|
+
API response containing task creation results for each input item. A typical shape is:
|
|
163
|
+
{
|
|
164
|
+
"status": "success",
|
|
165
|
+
"success_list": [{"video_name": "video_url"}],
|
|
166
|
+
"error_list": []
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
Constraints & Tips:
|
|
170
|
+
- Keep prompt concise and focused (建议 ≤ 500 字); too many details may distract the model.
|
|
171
|
+
- If using first/last frames, ensure their **aspect ratio matches** your chosen `--rt` to minimize cropping.
|
|
172
|
+
- If you must reproduce results, specify an explicit `--seed`.
|
|
173
|
+
- Unsupported parameters are ignored silently or may cause validation errors (model-specific).
|
|
174
|
+
|
|
175
|
+
Minimal examples:
|
|
176
|
+
1) Text-only batch of two 5-second clips at 720p, 16:9, 24 fps:
|
|
177
|
+
params = [
|
|
178
|
+
{
|
|
179
|
+
"video_name": "cat_park.mp4",
|
|
180
|
+
"prompt": "小猫骑着滑板穿过公园。 --rs 720p --rt 16:9 --dur 5 --fps 24 --wm false"
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"video_name": "city_night.mp4",
|
|
184
|
+
"prompt": "霓虹灯下的城市延时摄影风。 --rs 720p --rt 16:9 --dur 5 --fps 24 --seed 7"
|
|
185
|
+
},
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
2) With guided first/last frame (square, 6 s, camera fixed):
|
|
189
|
+
params = [
|
|
190
|
+
{
|
|
191
|
+
"video_name": "logo_reveal.mp4",
|
|
192
|
+
"first_frame": "https://cdn.example.com/brand/logo_start.png",
|
|
193
|
+
"last_frame": "https://cdn.example.com/brand/logo_end.png",
|
|
194
|
+
"prompt": "品牌 Logo 从线稿到上色的变化。 --rs 1080p --rt 1:1 --dur 6 --fps 24 --cf true"
|
|
195
|
+
}
|
|
196
|
+
]
|
|
197
|
+
"""
|
|
198
|
+
success_list = []
|
|
199
|
+
error_list = []
|
|
200
|
+
logger.debug(f"Using model: {getenv('MODEL_VIDEO_NAME', DEFAULT_VIDEO_MODEL_NAME)}")
|
|
201
|
+
logger.debug(f"video_generate params: {params}")
|
|
202
|
+
|
|
203
|
+
for start_idx in range(0, len(params), batch_size):
|
|
204
|
+
batch = params[start_idx : start_idx + batch_size]
|
|
205
|
+
logger.debug(f"video_generate batch {start_idx // batch_size}: {batch}")
|
|
206
|
+
|
|
207
|
+
task_dict = {}
|
|
208
|
+
tracer = trace.get_tracer("gcp.vertex.agent")
|
|
209
|
+
with tracer.start_as_current_span("call_llm") as span:
|
|
210
|
+
input_part = {"role": "user"}
|
|
211
|
+
output_part = {"message.role": "model"}
|
|
212
|
+
total_tokens = 0
|
|
213
|
+
for idx, item in enumerate(batch):
|
|
214
|
+
input_part[f"parts.{idx}.type"] = "text"
|
|
215
|
+
input_part[f"parts.{idx}.text"] = json.dumps(item, ensure_ascii=False)
|
|
216
|
+
|
|
217
|
+
video_name = item["video_name"]
|
|
218
|
+
prompt = item["prompt"]
|
|
219
|
+
first_frame = item.get("first_frame", None)
|
|
220
|
+
last_frame = item.get("last_frame", None)
|
|
221
|
+
try:
|
|
222
|
+
if not first_frame:
|
|
223
|
+
logger.debug(
|
|
224
|
+
f"video_generate task_{idx} text generation: prompt={prompt}"
|
|
225
|
+
)
|
|
226
|
+
response = await generate(prompt)
|
|
227
|
+
elif not last_frame:
|
|
228
|
+
logger.debug(
|
|
229
|
+
f"video_generate task_{idx} first frame generation: prompt={prompt}, first_frame={first_frame}"
|
|
230
|
+
)
|
|
231
|
+
response = await generate(prompt, first_frame)
|
|
232
|
+
else:
|
|
233
|
+
logger.debug(
|
|
234
|
+
f"video_generate task_{idx} first and last frame generation: prompt={prompt}, first_frame={first_frame}, last_frame={last_frame}"
|
|
235
|
+
)
|
|
236
|
+
response = await generate(prompt, first_frame, last_frame)
|
|
237
|
+
logger.debug(
|
|
238
|
+
f"batch_{start_idx // batch_size} video_generate task_{idx} response: {response}"
|
|
239
|
+
)
|
|
240
|
+
task_dict[response.id] = video_name
|
|
241
|
+
except Exception as e:
|
|
242
|
+
logger.error(f"Error: {e}")
|
|
243
|
+
error_list.append(video_name)
|
|
244
|
+
continue
|
|
245
|
+
|
|
246
|
+
logger.debug("begin query video_generate task status...")
|
|
247
|
+
|
|
248
|
+
while True:
|
|
249
|
+
task_list = list(task_dict.keys())
|
|
250
|
+
if len(task_list) == 0:
|
|
251
|
+
break
|
|
252
|
+
for idx, task_id in enumerate(task_list):
|
|
253
|
+
result = client.content_generation.tasks.get(task_id=task_id)
|
|
254
|
+
status = result.status
|
|
255
|
+
if status == "succeeded":
|
|
256
|
+
logger.debug(
|
|
257
|
+
f"{task_dict[task_id]} video_generate {status}. Video URL: {result.content.video_url}"
|
|
258
|
+
)
|
|
259
|
+
tool_context.state[f"{task_dict[task_id]}_video_url"] = (
|
|
260
|
+
result.content.video_url
|
|
261
|
+
)
|
|
262
|
+
total_tokens += result.usage.completion_tokens
|
|
263
|
+
output_part[f"message.parts.{idx}.type"] = "text"
|
|
264
|
+
output_part[f"message.parts.{idx}.text"] = (
|
|
265
|
+
f"{task_dict[task_id]}: {result.content.video_url}"
|
|
266
|
+
)
|
|
267
|
+
success_list.append(
|
|
268
|
+
{task_dict[task_id]: result.content.video_url}
|
|
269
|
+
)
|
|
270
|
+
task_dict.pop(task_id, None)
|
|
271
|
+
elif status == "failed":
|
|
272
|
+
logger.error(
|
|
273
|
+
f"{task_dict[task_id]} video_generate {status}. Error: {result.error}"
|
|
274
|
+
)
|
|
275
|
+
error_list.append(task_dict[task_id])
|
|
276
|
+
task_dict.pop(task_id, None)
|
|
277
|
+
else:
|
|
278
|
+
logger.debug(
|
|
279
|
+
f"{task_dict[task_id]} video_generate current status: {status}, Retrying after 10 seconds..."
|
|
280
|
+
)
|
|
281
|
+
time.sleep(10)
|
|
282
|
+
|
|
283
|
+
add_span_attributes(
|
|
284
|
+
span,
|
|
285
|
+
tool_context,
|
|
286
|
+
input_part=input_part,
|
|
287
|
+
output_part=output_part,
|
|
288
|
+
output_tokens=total_tokens,
|
|
289
|
+
total_tokens=total_tokens,
|
|
290
|
+
request_model=getenv("MODEL_VIDEO_NAME", DEFAULT_VIDEO_MODEL_NAME),
|
|
291
|
+
response_model=getenv("MODEL_VIDEO_NAME", DEFAULT_VIDEO_MODEL_NAME),
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
if len(success_list) == 0:
|
|
295
|
+
logger.debug(
|
|
296
|
+
f"video_generate success_list: {success_list}\nerror_list: {error_list}"
|
|
297
|
+
)
|
|
298
|
+
return {
|
|
299
|
+
"status": "error",
|
|
300
|
+
"success_list": success_list,
|
|
301
|
+
"error_list": error_list,
|
|
302
|
+
}
|
|
303
|
+
else:
|
|
304
|
+
logger.debug(
|
|
305
|
+
f"video_generate success_list: {success_list}\nerror_list: {error_list}"
|
|
306
|
+
)
|
|
307
|
+
return {
|
|
308
|
+
"status": "success",
|
|
309
|
+
"success_list": success_list,
|
|
310
|
+
"error_list": error_list,
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def add_span_attributes(
|
|
315
|
+
span: Span,
|
|
316
|
+
tool_context: ToolContext,
|
|
317
|
+
input_part: dict | None = None,
|
|
318
|
+
output_part: dict | None = None,
|
|
319
|
+
input_tokens: int | None = None,
|
|
320
|
+
output_tokens: int | None = None,
|
|
321
|
+
total_tokens: int | None = None,
|
|
322
|
+
request_model: str | None = None,
|
|
323
|
+
response_model: str | None = None,
|
|
324
|
+
):
|
|
325
|
+
try:
|
|
326
|
+
# common attributes
|
|
327
|
+
app_name = tool_context._invocation_context.app_name
|
|
328
|
+
user_id = tool_context._invocation_context.user_id
|
|
329
|
+
agent_name = tool_context.agent_name
|
|
330
|
+
session_id = tool_context._invocation_context.session.id
|
|
331
|
+
span.set_attribute("gen_ai.agent.name", agent_name)
|
|
332
|
+
span.set_attribute("openinference.instrumentation.veadk", VERSION)
|
|
333
|
+
span.set_attribute("gen_ai.app.name", app_name)
|
|
334
|
+
span.set_attribute("gen_ai.user.id", user_id)
|
|
335
|
+
span.set_attribute("gen_ai.session.id", session_id)
|
|
336
|
+
span.set_attribute("agent_name", agent_name)
|
|
337
|
+
span.set_attribute("agent.name", agent_name)
|
|
338
|
+
span.set_attribute("app_name", app_name)
|
|
339
|
+
span.set_attribute("app.name", app_name)
|
|
340
|
+
span.set_attribute("user.id", user_id)
|
|
341
|
+
span.set_attribute("session.id", session_id)
|
|
342
|
+
span.set_attribute("cozeloop.report.source", "veadk")
|
|
343
|
+
|
|
344
|
+
# llm attributes
|
|
345
|
+
span.set_attribute("gen_ai.system", "openai")
|
|
346
|
+
span.set_attribute("gen_ai.operation.name", "chat")
|
|
347
|
+
if request_model:
|
|
348
|
+
span.set_attribute("gen_ai.request.model", request_model)
|
|
349
|
+
if response_model:
|
|
350
|
+
span.set_attribute("gen_ai.response.model", response_model)
|
|
351
|
+
if total_tokens:
|
|
352
|
+
span.set_attribute("gen_ai.usage.total_tokens", total_tokens)
|
|
353
|
+
if output_tokens:
|
|
354
|
+
span.set_attribute("gen_ai.usage.output_tokens", output_tokens)
|
|
355
|
+
if input_tokens:
|
|
356
|
+
span.set_attribute("gen_ai.usage.input_tokens", input_tokens)
|
|
357
|
+
if input_part:
|
|
358
|
+
span.add_event("gen_ai.user.message", input_part)
|
|
359
|
+
if output_part:
|
|
360
|
+
span.add_event("gen_ai.choice", output_part)
|
|
361
|
+
|
|
362
|
+
except Exception:
|
|
363
|
+
traceback.print_exc()
|