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.
Files changed (218) hide show
  1. veadk/__init__.py +37 -0
  2. veadk/a2a/__init__.py +13 -0
  3. veadk/a2a/agent_card.py +45 -0
  4. veadk/a2a/remote_ve_agent.py +390 -0
  5. veadk/a2a/utils/__init__.py +13 -0
  6. veadk/a2a/utils/agent_to_a2a.py +170 -0
  7. veadk/a2a/ve_a2a_server.py +93 -0
  8. veadk/a2a/ve_agent_executor.py +78 -0
  9. veadk/a2a/ve_middlewares.py +313 -0
  10. veadk/a2a/ve_task_store.py +37 -0
  11. veadk/agent.py +402 -0
  12. veadk/agent_builder.py +93 -0
  13. veadk/agents/loop_agent.py +68 -0
  14. veadk/agents/parallel_agent.py +72 -0
  15. veadk/agents/sequential_agent.py +64 -0
  16. veadk/auth/__init__.py +13 -0
  17. veadk/auth/base_auth.py +22 -0
  18. veadk/auth/ve_credential_service.py +203 -0
  19. veadk/auth/veauth/__init__.py +13 -0
  20. veadk/auth/veauth/apmplus_veauth.py +58 -0
  21. veadk/auth/veauth/ark_veauth.py +75 -0
  22. veadk/auth/veauth/base_veauth.py +50 -0
  23. veadk/auth/veauth/cozeloop_veauth.py +13 -0
  24. veadk/auth/veauth/opensearch_veauth.py +75 -0
  25. veadk/auth/veauth/postgresql_veauth.py +75 -0
  26. veadk/auth/veauth/prompt_pilot_veauth.py +60 -0
  27. veadk/auth/veauth/speech_veauth.py +54 -0
  28. veadk/auth/veauth/utils.py +69 -0
  29. veadk/auth/veauth/vesearch_veauth.py +62 -0
  30. veadk/auth/veauth/viking_mem0_veauth.py +91 -0
  31. veadk/cli/__init__.py +13 -0
  32. veadk/cli/cli.py +58 -0
  33. veadk/cli/cli_clean.py +87 -0
  34. veadk/cli/cli_create.py +163 -0
  35. veadk/cli/cli_deploy.py +233 -0
  36. veadk/cli/cli_eval.py +215 -0
  37. veadk/cli/cli_init.py +214 -0
  38. veadk/cli/cli_kb.py +110 -0
  39. veadk/cli/cli_pipeline.py +285 -0
  40. veadk/cli/cli_prompt.py +86 -0
  41. veadk/cli/cli_update.py +106 -0
  42. veadk/cli/cli_uploadevalset.py +139 -0
  43. veadk/cli/cli_web.py +143 -0
  44. veadk/cloud/__init__.py +13 -0
  45. veadk/cloud/cloud_agent_engine.py +485 -0
  46. veadk/cloud/cloud_app.py +475 -0
  47. veadk/config.py +115 -0
  48. veadk/configs/__init__.py +13 -0
  49. veadk/configs/auth_configs.py +133 -0
  50. veadk/configs/database_configs.py +132 -0
  51. veadk/configs/model_configs.py +78 -0
  52. veadk/configs/tool_configs.py +54 -0
  53. veadk/configs/tracing_configs.py +110 -0
  54. veadk/consts.py +74 -0
  55. veadk/evaluation/__init__.py +17 -0
  56. veadk/evaluation/adk_evaluator/__init__.py +17 -0
  57. veadk/evaluation/adk_evaluator/adk_evaluator.py +302 -0
  58. veadk/evaluation/base_evaluator.py +642 -0
  59. veadk/evaluation/deepeval_evaluator/__init__.py +17 -0
  60. veadk/evaluation/deepeval_evaluator/deepeval_evaluator.py +339 -0
  61. veadk/evaluation/eval_set_file_loader.py +48 -0
  62. veadk/evaluation/eval_set_recorder.py +146 -0
  63. veadk/evaluation/types.py +65 -0
  64. veadk/evaluation/utils/prometheus.py +196 -0
  65. veadk/integrations/__init__.py +13 -0
  66. veadk/integrations/ve_apig/__init__.py +13 -0
  67. veadk/integrations/ve_apig/ve_apig.py +349 -0
  68. veadk/integrations/ve_apig/ve_apig_utils.py +332 -0
  69. veadk/integrations/ve_code_pipeline/__init__.py +13 -0
  70. veadk/integrations/ve_code_pipeline/ve_code_pipeline.py +431 -0
  71. veadk/integrations/ve_cozeloop/__init__.py +13 -0
  72. veadk/integrations/ve_cozeloop/ve_cozeloop.py +96 -0
  73. veadk/integrations/ve_cr/__init__.py +13 -0
  74. veadk/integrations/ve_cr/ve_cr.py +220 -0
  75. veadk/integrations/ve_faas/__init__.py +13 -0
  76. veadk/integrations/ve_faas/template/cookiecutter.json +15 -0
  77. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/__init__.py +13 -0
  78. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
  79. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/config.yaml.example +6 -0
  80. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/deploy.py +106 -0
  81. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__init__.py +13 -0
  82. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/agent.py +25 -0
  83. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/app.py +202 -0
  84. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/requirements.txt +3 -0
  85. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/run.sh +49 -0
  86. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name }}/__init__.py +14 -0
  87. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name }}/agent.py +27 -0
  88. veadk/integrations/ve_faas/ve_faas.py +754 -0
  89. veadk/integrations/ve_faas/ve_faas_utils.py +408 -0
  90. veadk/integrations/ve_faas/web_template/cookiecutter.json +20 -0
  91. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/__init__.py +13 -0
  92. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
  93. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/config.yaml.example +2 -0
  94. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/deploy.py +44 -0
  95. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/Dockerfile +23 -0
  96. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/app.py +123 -0
  97. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/init_db.py +46 -0
  98. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/models.py +36 -0
  99. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/requirements.txt +4 -0
  100. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/run.sh +21 -0
  101. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/css/style.css +368 -0
  102. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/js/admin.js +0 -0
  103. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/dashboard.html +21 -0
  104. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/edit_post.html +24 -0
  105. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/login.html +21 -0
  106. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/posts.html +53 -0
  107. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/base.html +45 -0
  108. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/index.html +29 -0
  109. veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/post.html +14 -0
  110. veadk/integrations/ve_identity/__init__.py +110 -0
  111. veadk/integrations/ve_identity/auth_config.py +261 -0
  112. veadk/integrations/ve_identity/auth_mixins.py +650 -0
  113. veadk/integrations/ve_identity/auth_processor.py +385 -0
  114. veadk/integrations/ve_identity/function_tool.py +158 -0
  115. veadk/integrations/ve_identity/identity_client.py +864 -0
  116. veadk/integrations/ve_identity/mcp_tool.py +181 -0
  117. veadk/integrations/ve_identity/mcp_toolset.py +431 -0
  118. veadk/integrations/ve_identity/models.py +228 -0
  119. veadk/integrations/ve_identity/token_manager.py +188 -0
  120. veadk/integrations/ve_identity/utils.py +151 -0
  121. veadk/integrations/ve_prompt_pilot/__init__.py +13 -0
  122. veadk/integrations/ve_prompt_pilot/ve_prompt_pilot.py +85 -0
  123. veadk/integrations/ve_tls/__init__.py +13 -0
  124. veadk/integrations/ve_tls/utils.py +116 -0
  125. veadk/integrations/ve_tls/ve_tls.py +212 -0
  126. veadk/integrations/ve_tos/ve_tos.py +710 -0
  127. veadk/integrations/ve_viking_db_memory/__init__.py +13 -0
  128. veadk/integrations/ve_viking_db_memory/ve_viking_db_memory.py +308 -0
  129. veadk/knowledgebase/__init__.py +17 -0
  130. veadk/knowledgebase/backends/__init__.py +13 -0
  131. veadk/knowledgebase/backends/base_backend.py +72 -0
  132. veadk/knowledgebase/backends/in_memory_backend.py +91 -0
  133. veadk/knowledgebase/backends/opensearch_backend.py +162 -0
  134. veadk/knowledgebase/backends/redis_backend.py +172 -0
  135. veadk/knowledgebase/backends/utils.py +92 -0
  136. veadk/knowledgebase/backends/vikingdb_knowledge_backend.py +608 -0
  137. veadk/knowledgebase/entry.py +25 -0
  138. veadk/knowledgebase/knowledgebase.py +307 -0
  139. veadk/memory/__init__.py +35 -0
  140. veadk/memory/long_term_memory.py +365 -0
  141. veadk/memory/long_term_memory_backends/__init__.py +13 -0
  142. veadk/memory/long_term_memory_backends/base_backend.py +35 -0
  143. veadk/memory/long_term_memory_backends/in_memory_backend.py +67 -0
  144. veadk/memory/long_term_memory_backends/mem0_backend.py +155 -0
  145. veadk/memory/long_term_memory_backends/opensearch_backend.py +124 -0
  146. veadk/memory/long_term_memory_backends/redis_backend.py +140 -0
  147. veadk/memory/long_term_memory_backends/vikingdb_memory_backend.py +189 -0
  148. veadk/memory/short_term_memory.py +252 -0
  149. veadk/memory/short_term_memory_backends/__init__.py +13 -0
  150. veadk/memory/short_term_memory_backends/base_backend.py +31 -0
  151. veadk/memory/short_term_memory_backends/mysql_backend.py +49 -0
  152. veadk/memory/short_term_memory_backends/postgresql_backend.py +49 -0
  153. veadk/memory/short_term_memory_backends/sqlite_backend.py +55 -0
  154. veadk/memory/short_term_memory_processor.py +100 -0
  155. veadk/processors/__init__.py +26 -0
  156. veadk/processors/base_run_processor.py +120 -0
  157. veadk/prompts/__init__.py +13 -0
  158. veadk/prompts/agent_default_prompt.py +30 -0
  159. veadk/prompts/prompt_evaluator.py +20 -0
  160. veadk/prompts/prompt_memory_processor.py +55 -0
  161. veadk/prompts/prompt_optimization.py +150 -0
  162. veadk/runner.py +732 -0
  163. veadk/tools/__init__.py +13 -0
  164. veadk/tools/builtin_tools/__init__.py +13 -0
  165. veadk/tools/builtin_tools/agent_authorization.py +94 -0
  166. veadk/tools/builtin_tools/generate_image.py +23 -0
  167. veadk/tools/builtin_tools/image_edit.py +300 -0
  168. veadk/tools/builtin_tools/image_generate.py +446 -0
  169. veadk/tools/builtin_tools/lark.py +67 -0
  170. veadk/tools/builtin_tools/las.py +24 -0
  171. veadk/tools/builtin_tools/link_reader.py +66 -0
  172. veadk/tools/builtin_tools/llm_shield.py +381 -0
  173. veadk/tools/builtin_tools/load_knowledgebase.py +97 -0
  174. veadk/tools/builtin_tools/mcp_router.py +29 -0
  175. veadk/tools/builtin_tools/run_code.py +113 -0
  176. veadk/tools/builtin_tools/tts.py +253 -0
  177. veadk/tools/builtin_tools/vesearch.py +49 -0
  178. veadk/tools/builtin_tools/video_generate.py +363 -0
  179. veadk/tools/builtin_tools/web_scraper.py +76 -0
  180. veadk/tools/builtin_tools/web_search.py +83 -0
  181. veadk/tools/demo_tools.py +58 -0
  182. veadk/tools/load_knowledgebase_tool.py +149 -0
  183. veadk/tools/sandbox/__init__.py +13 -0
  184. veadk/tools/sandbox/browser_sandbox.py +37 -0
  185. veadk/tools/sandbox/code_sandbox.py +40 -0
  186. veadk/tools/sandbox/computer_sandbox.py +34 -0
  187. veadk/tracing/__init__.py +13 -0
  188. veadk/tracing/base_tracer.py +58 -0
  189. veadk/tracing/telemetry/__init__.py +13 -0
  190. veadk/tracing/telemetry/attributes/attributes.py +29 -0
  191. veadk/tracing/telemetry/attributes/extractors/common_attributes_extractors.py +180 -0
  192. veadk/tracing/telemetry/attributes/extractors/llm_attributes_extractors.py +858 -0
  193. veadk/tracing/telemetry/attributes/extractors/tool_attributes_extractors.py +152 -0
  194. veadk/tracing/telemetry/attributes/extractors/types.py +164 -0
  195. veadk/tracing/telemetry/exporters/__init__.py +13 -0
  196. veadk/tracing/telemetry/exporters/apmplus_exporter.py +558 -0
  197. veadk/tracing/telemetry/exporters/base_exporter.py +39 -0
  198. veadk/tracing/telemetry/exporters/cozeloop_exporter.py +129 -0
  199. veadk/tracing/telemetry/exporters/inmemory_exporter.py +248 -0
  200. veadk/tracing/telemetry/exporters/tls_exporter.py +139 -0
  201. veadk/tracing/telemetry/opentelemetry_tracer.py +320 -0
  202. veadk/tracing/telemetry/telemetry.py +411 -0
  203. veadk/types.py +47 -0
  204. veadk/utils/__init__.py +13 -0
  205. veadk/utils/audio_manager.py +95 -0
  206. veadk/utils/auth.py +294 -0
  207. veadk/utils/logger.py +59 -0
  208. veadk/utils/mcp_utils.py +44 -0
  209. veadk/utils/misc.py +184 -0
  210. veadk/utils/patches.py +101 -0
  211. veadk/utils/volcengine_sign.py +205 -0
  212. veadk/version.py +15 -0
  213. veadk_python-0.2.27.dist-info/METADATA +373 -0
  214. veadk_python-0.2.27.dist-info/RECORD +218 -0
  215. veadk_python-0.2.27.dist-info/WHEEL +5 -0
  216. veadk_python-0.2.27.dist-info/entry_points.txt +2 -0
  217. veadk_python-0.2.27.dist-info/licenses/LICENSE +201 -0
  218. 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()