veadk-python 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of veadk-python might be problematic. Click here for more details.
- veadk/__init__.py +31 -0
- veadk/a2a/__init__.py +13 -0
- veadk/a2a/agent_card.py +45 -0
- veadk/a2a/remote_ve_agent.py +19 -0
- veadk/a2a/ve_a2a_server.py +77 -0
- veadk/a2a/ve_agent_executor.py +78 -0
- veadk/a2a/ve_task_store.py +37 -0
- veadk/agent.py +253 -0
- veadk/cli/__init__.py +13 -0
- veadk/cli/main.py +278 -0
- veadk/cli/services/agentpilot/__init__.py +17 -0
- veadk/cli/services/agentpilot/agentpilot.py +77 -0
- veadk/cli/services/veapig/__init__.py +17 -0
- veadk/cli/services/veapig/apig.py +224 -0
- veadk/cli/services/veapig/apig_utils.py +332 -0
- veadk/cli/services/vefaas/__init__.py +17 -0
- veadk/cli/services/vefaas/template/deploy.py +44 -0
- veadk/cli/services/vefaas/template/src/app.py +30 -0
- veadk/cli/services/vefaas/template/src/config.py +58 -0
- veadk/cli/services/vefaas/vefaas.py +346 -0
- veadk/cli/services/vefaas/vefaas_utils.py +408 -0
- veadk/cli/services/vetls/__init__.py +17 -0
- veadk/cli/services/vetls/vetls.py +87 -0
- veadk/cli/studio/__init__.py +13 -0
- veadk/cli/studio/agent_processor.py +247 -0
- veadk/cli/studio/fast_api.py +232 -0
- veadk/cli/studio/model.py +116 -0
- veadk/cloud/__init__.py +13 -0
- veadk/cloud/cloud_agent_engine.py +144 -0
- veadk/cloud/cloud_app.py +123 -0
- veadk/cloud/template/app.py +30 -0
- veadk/cloud/template/config.py +55 -0
- veadk/config.py +131 -0
- veadk/consts.py +17 -0
- veadk/database/__init__.py +17 -0
- veadk/database/base_database.py +45 -0
- veadk/database/database_factory.py +80 -0
- veadk/database/kv/__init__.py +13 -0
- veadk/database/kv/redis_database.py +109 -0
- veadk/database/local_database.py +43 -0
- veadk/database/relational/__init__.py +13 -0
- veadk/database/relational/mysql_database.py +114 -0
- veadk/database/vector/__init__.py +13 -0
- veadk/database/vector/opensearch_vector_database.py +205 -0
- veadk/database/vector/type.py +50 -0
- veadk/database/viking/__init__.py +13 -0
- veadk/database/viking/viking_database.py +378 -0
- veadk/database/viking/viking_memory_db.py +521 -0
- veadk/evaluation/__init__.py +17 -0
- veadk/evaluation/adk_evaluator/__init__.py +13 -0
- veadk/evaluation/adk_evaluator/adk_evaluator.py +291 -0
- veadk/evaluation/base_evaluator.py +242 -0
- veadk/evaluation/deepeval_evaluator/__init__.py +17 -0
- veadk/evaluation/deepeval_evaluator/deepeval_evaluator.py +223 -0
- veadk/evaluation/eval_set_file_loader.py +28 -0
- veadk/evaluation/eval_set_recorder.py +91 -0
- veadk/evaluation/utils/prometheus.py +142 -0
- veadk/knowledgebase/__init__.py +17 -0
- veadk/knowledgebase/knowledgebase.py +83 -0
- veadk/knowledgebase/knowledgebase_database_adapter.py +259 -0
- veadk/memory/__init__.py +13 -0
- veadk/memory/long_term_memory.py +119 -0
- veadk/memory/memory_database_adapter.py +235 -0
- veadk/memory/short_term_memory.py +124 -0
- veadk/memory/short_term_memory_processor.py +90 -0
- veadk/prompts/__init__.py +13 -0
- veadk/prompts/agent_default_prompt.py +30 -0
- veadk/prompts/prompt_evaluator.py +20 -0
- veadk/prompts/prompt_memory_processor.py +55 -0
- veadk/prompts/prompt_optimization.py +158 -0
- veadk/runner.py +252 -0
- veadk/tools/__init__.py +13 -0
- veadk/tools/builtin_tools/__init__.py +13 -0
- veadk/tools/builtin_tools/lark.py +67 -0
- veadk/tools/builtin_tools/las.py +23 -0
- veadk/tools/builtin_tools/vesearch.py +49 -0
- veadk/tools/builtin_tools/web_scraper.py +76 -0
- veadk/tools/builtin_tools/web_search.py +192 -0
- veadk/tools/demo_tools.py +58 -0
- veadk/tools/load_knowledgebase_tool.py +144 -0
- veadk/tools/sandbox/__init__.py +13 -0
- veadk/tools/sandbox/browser_sandbox.py +27 -0
- veadk/tools/sandbox/code_sandbox.py +30 -0
- veadk/tools/sandbox/computer_sandbox.py +27 -0
- veadk/tracing/__init__.py +13 -0
- veadk/tracing/base_tracer.py +172 -0
- veadk/tracing/telemetry/__init__.py +13 -0
- veadk/tracing/telemetry/exporters/__init__.py +13 -0
- veadk/tracing/telemetry/exporters/apiserver_exporter.py +60 -0
- veadk/tracing/telemetry/exporters/apmplus_exporter.py +101 -0
- veadk/tracing/telemetry/exporters/base_exporter.py +28 -0
- veadk/tracing/telemetry/exporters/cozeloop_exporter.py +69 -0
- veadk/tracing/telemetry/exporters/inmemory_exporter.py +88 -0
- veadk/tracing/telemetry/exporters/tls_exporter.py +78 -0
- veadk/tracing/telemetry/metrics/__init__.py +13 -0
- veadk/tracing/telemetry/metrics/opentelemetry_metrics.py +73 -0
- veadk/tracing/telemetry/opentelemetry_tracer.py +167 -0
- veadk/types.py +23 -0
- veadk/utils/__init__.py +13 -0
- veadk/utils/logger.py +59 -0
- veadk/utils/misc.py +33 -0
- veadk/utils/patches.py +85 -0
- veadk/utils/volcengine_sign.py +199 -0
- veadk/version.py +15 -0
- veadk_python-0.1.0.dist-info/METADATA +124 -0
- veadk_python-0.1.0.dist-info/RECORD +110 -0
- veadk_python-0.1.0.dist-info/WHEEL +5 -0
- veadk_python-0.1.0.dist-info/entry_points.txt +2 -0
- veadk_python-0.1.0.dist-info/licenses/LICENSE +201 -0
- veadk_python-0.1.0.dist-info/top_level.txt +1 -0
veadk/runner.py
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
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
|
+
from typing import Union
|
|
15
|
+
|
|
16
|
+
from google.adk.agents import RunConfig
|
|
17
|
+
from google.adk.agents.run_config import StreamingMode
|
|
18
|
+
from google.adk.runners import Runner as ADKRunner
|
|
19
|
+
from google.genai import types
|
|
20
|
+
from google.genai.types import Blob
|
|
21
|
+
|
|
22
|
+
from veadk.a2a.remote_ve_agent import RemoteVeAgent
|
|
23
|
+
from veadk.agent import Agent
|
|
24
|
+
from veadk.evaluation import EvalSetRecorder
|
|
25
|
+
from veadk.memory.short_term_memory import ShortTermMemory
|
|
26
|
+
from veadk.types import MediaMessage
|
|
27
|
+
from veadk.utils.logger import get_logger
|
|
28
|
+
from veadk.utils.misc import read_png_to_bytes
|
|
29
|
+
|
|
30
|
+
logger = get_logger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
RunnerMessage = Union[
|
|
34
|
+
str, # single turn text-based prompt
|
|
35
|
+
list[str], # multiple turn text-based prompt
|
|
36
|
+
MediaMessage, # single turn prompt with media
|
|
37
|
+
list[MediaMessage], # multiple turn prompt with media
|
|
38
|
+
list[MediaMessage | str], # multiple turn prompt with media and text-based prompt
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class Runner:
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
agent: Agent | RemoteVeAgent,
|
|
46
|
+
short_term_memory: ShortTermMemory,
|
|
47
|
+
app_name: str = "veadk_default_app",
|
|
48
|
+
user_id: str = "veadk_default_user",
|
|
49
|
+
):
|
|
50
|
+
# basic settings
|
|
51
|
+
self.app_name = app_name
|
|
52
|
+
self.user_id = user_id
|
|
53
|
+
|
|
54
|
+
# agent settings
|
|
55
|
+
self.agent = agent
|
|
56
|
+
|
|
57
|
+
self.short_term_memory = short_term_memory
|
|
58
|
+
self.session_service = short_term_memory.session_service
|
|
59
|
+
|
|
60
|
+
if isinstance(self.agent, Agent):
|
|
61
|
+
self.long_term_memory = self.agent.long_term_memory
|
|
62
|
+
else:
|
|
63
|
+
self.long_term_memory = None
|
|
64
|
+
|
|
65
|
+
# maintain a in-memory runner for fast inference
|
|
66
|
+
self.runner = ADKRunner(
|
|
67
|
+
app_name=self.app_name,
|
|
68
|
+
agent=self.agent,
|
|
69
|
+
session_service=self.session_service,
|
|
70
|
+
memory_service=self.long_term_memory,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def _convert_messages(self, messages) -> list:
|
|
74
|
+
if isinstance(messages, str):
|
|
75
|
+
messages = [types.Content(role="user", parts=[types.Part(text=messages)])]
|
|
76
|
+
elif isinstance(messages, MediaMessage):
|
|
77
|
+
assert messages.media.endswith(".png"), (
|
|
78
|
+
"The MediaMessage only supports PNG format file for now."
|
|
79
|
+
)
|
|
80
|
+
messages = [
|
|
81
|
+
types.Content(
|
|
82
|
+
role="user",
|
|
83
|
+
parts=[
|
|
84
|
+
types.Part(text=messages.text),
|
|
85
|
+
types.Part(
|
|
86
|
+
inline_data=Blob(
|
|
87
|
+
display_name=messages.media,
|
|
88
|
+
data=read_png_to_bytes(messages.media),
|
|
89
|
+
mime_type="image/png",
|
|
90
|
+
)
|
|
91
|
+
),
|
|
92
|
+
],
|
|
93
|
+
)
|
|
94
|
+
]
|
|
95
|
+
elif isinstance(messages, list):
|
|
96
|
+
converted_messages = []
|
|
97
|
+
for message in messages:
|
|
98
|
+
converted_messages.extend(self._convert_messages(message))
|
|
99
|
+
messages = converted_messages
|
|
100
|
+
else:
|
|
101
|
+
raise ValueError(f"Unknown message type: {type(messages)}")
|
|
102
|
+
|
|
103
|
+
return messages
|
|
104
|
+
|
|
105
|
+
async def _run(
|
|
106
|
+
self,
|
|
107
|
+
session_id: str,
|
|
108
|
+
message: types.Content,
|
|
109
|
+
stream: bool = False,
|
|
110
|
+
):
|
|
111
|
+
stream_mode = StreamingMode.SSE if stream else StreamingMode.NONE
|
|
112
|
+
|
|
113
|
+
async def event_generator():
|
|
114
|
+
async for event in self.runner.run_async(
|
|
115
|
+
user_id=self.user_id,
|
|
116
|
+
session_id=session_id,
|
|
117
|
+
new_message=message,
|
|
118
|
+
run_config=RunConfig(streaming_mode=stream_mode),
|
|
119
|
+
):
|
|
120
|
+
if event.get_function_calls():
|
|
121
|
+
for function_call in event.get_function_calls():
|
|
122
|
+
logger.debug(f"Function call: {function_call}")
|
|
123
|
+
elif (
|
|
124
|
+
event.content is not None
|
|
125
|
+
and event.content.parts[0].text is not None
|
|
126
|
+
and len(event.content.parts[0].text.strip()) > 0
|
|
127
|
+
):
|
|
128
|
+
yield event.content.parts[0].text
|
|
129
|
+
|
|
130
|
+
final_output = ""
|
|
131
|
+
async for chunk in event_generator():
|
|
132
|
+
if stream:
|
|
133
|
+
print(chunk, end="", flush=True)
|
|
134
|
+
final_output += chunk
|
|
135
|
+
if stream:
|
|
136
|
+
print() # end with a new line
|
|
137
|
+
|
|
138
|
+
return final_output
|
|
139
|
+
|
|
140
|
+
async def run(
|
|
141
|
+
self,
|
|
142
|
+
messages: RunnerMessage,
|
|
143
|
+
session_id: str,
|
|
144
|
+
stream: bool = False,
|
|
145
|
+
):
|
|
146
|
+
messages: list = self._convert_messages(messages)
|
|
147
|
+
|
|
148
|
+
await self.short_term_memory.create_session(
|
|
149
|
+
app_name=self.app_name, user_id=self.user_id, session_id=session_id
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
logger.info("Begin to process user messages.")
|
|
153
|
+
|
|
154
|
+
final_output = ""
|
|
155
|
+
for message in messages:
|
|
156
|
+
final_output = await self._run(session_id, message, stream)
|
|
157
|
+
|
|
158
|
+
# try to save tracing file
|
|
159
|
+
if isinstance(self.agent, Agent):
|
|
160
|
+
self.save_tracing_file(session_id)
|
|
161
|
+
|
|
162
|
+
return final_output
|
|
163
|
+
|
|
164
|
+
def save_tracing_file(self, session_id: str) -> str:
|
|
165
|
+
if not self.agent.tracers:
|
|
166
|
+
return
|
|
167
|
+
|
|
168
|
+
try:
|
|
169
|
+
dump_path = ""
|
|
170
|
+
for tracer in self.agent.tracers:
|
|
171
|
+
dump_path = tracer.dump(self.user_id, session_id)
|
|
172
|
+
|
|
173
|
+
return dump_path
|
|
174
|
+
except Exception as e:
|
|
175
|
+
logger.error(f"Failed to save tracing file: {e}")
|
|
176
|
+
return ""
|
|
177
|
+
|
|
178
|
+
async def save_eval_set(self, session_id: str, eval_set_id: str = "default") -> str:
|
|
179
|
+
eval_set_recorder = EvalSetRecorder(self.session_service, eval_set_id)
|
|
180
|
+
eval_set_path = await eval_set_recorder.dump(
|
|
181
|
+
self.app_name, self.user_id, session_id
|
|
182
|
+
)
|
|
183
|
+
return eval_set_path
|
|
184
|
+
|
|
185
|
+
async def save_session_to_long_term_memory(self, session_id: str) -> None:
|
|
186
|
+
if not self.long_term_memory:
|
|
187
|
+
logger.warning("Long-term memory is not enabled. Failed to save session.")
|
|
188
|
+
return
|
|
189
|
+
|
|
190
|
+
session = await self.session_service.get_session(
|
|
191
|
+
app_name=self.app_name,
|
|
192
|
+
user_id=self.user_id,
|
|
193
|
+
session_id=session_id,
|
|
194
|
+
)
|
|
195
|
+
await self.long_term_memory.add_session_to_memory(session)
|
|
196
|
+
logger.info(f"Add session `{session.id}` to long-term memory.")
|
|
197
|
+
|
|
198
|
+
async def run_with_final_event(
|
|
199
|
+
self,
|
|
200
|
+
messages: RunnerMessage,
|
|
201
|
+
session_id: str,
|
|
202
|
+
):
|
|
203
|
+
"""non-streaming run with final event"""
|
|
204
|
+
messages: list = self._convert_messages(messages)
|
|
205
|
+
|
|
206
|
+
await self.short_term_memory.create_session(
|
|
207
|
+
app_name=self.app_name, user_id=self.user_id, session_id=session_id
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
logger.info("Begin to process user messages.")
|
|
211
|
+
|
|
212
|
+
final_event = ""
|
|
213
|
+
async for event in self.runner.run_async(
|
|
214
|
+
user_id=self.user_id, session_id=session_id, new_message=messages[0]
|
|
215
|
+
):
|
|
216
|
+
if event.get_function_calls():
|
|
217
|
+
for function_call in event.get_function_calls():
|
|
218
|
+
logger.debug(f"Function call: {function_call}")
|
|
219
|
+
elif (
|
|
220
|
+
not event.partial
|
|
221
|
+
and event.content.parts[0].text is not None
|
|
222
|
+
and len(event.content.parts[0].text.strip()) > 0
|
|
223
|
+
):
|
|
224
|
+
final_event = event.model_dump_json(exclude_none=True, by_alias=True)
|
|
225
|
+
|
|
226
|
+
return final_event
|
|
227
|
+
|
|
228
|
+
async def run_sse(
|
|
229
|
+
self,
|
|
230
|
+
session_id: str,
|
|
231
|
+
prompt: str,
|
|
232
|
+
):
|
|
233
|
+
message = types.Content(role="user", parts=[types.Part(text=prompt)])
|
|
234
|
+
|
|
235
|
+
await self.short_term_memory.create_session(
|
|
236
|
+
app_name=self.app_name, user_id=self.user_id, session_id=session_id
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
logger.info("Begin to process user messages under SSE method.")
|
|
240
|
+
|
|
241
|
+
async for event in self.runner.run_async(
|
|
242
|
+
user_id=self.user_id,
|
|
243
|
+
session_id=session_id,
|
|
244
|
+
new_message=message,
|
|
245
|
+
run_config=RunConfig(streaming_mode=StreamingMode.SSE),
|
|
246
|
+
):
|
|
247
|
+
# Format as SSE data
|
|
248
|
+
sse_event = event.model_dump_json(exclude_none=True, by_alias=True)
|
|
249
|
+
if event.get_function_calls():
|
|
250
|
+
for function_call in event.get_function_calls():
|
|
251
|
+
logger.debug(f"SSE function call event: {sse_event}")
|
|
252
|
+
yield f"data: {sse_event}\n\n"
|
veadk/tools/__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,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,67 @@
|
|
|
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 subprocess
|
|
16
|
+
|
|
17
|
+
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset, StdioServerParameters
|
|
18
|
+
|
|
19
|
+
from veadk.config import getenv
|
|
20
|
+
from veadk.utils.logger import get_logger
|
|
21
|
+
|
|
22
|
+
logger = get_logger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def check_env():
|
|
26
|
+
try:
|
|
27
|
+
result = subprocess.run(
|
|
28
|
+
["npx", "-v"], capture_output=True, text=True, check=True
|
|
29
|
+
)
|
|
30
|
+
version = result.stdout.strip()
|
|
31
|
+
logger.info(f"Check `npx` command done, version: {version}")
|
|
32
|
+
except Exception as e:
|
|
33
|
+
raise Exception(
|
|
34
|
+
"Check `npx` command failed. Please install `npx` command manually."
|
|
35
|
+
) from e
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
result = subprocess.run(
|
|
39
|
+
["lark-mcp", "-V"], capture_output=True, text=True, check=True
|
|
40
|
+
)
|
|
41
|
+
version = result.stdout.strip()
|
|
42
|
+
logger.info(f"Check `lark-mcp` command done, version: {version}")
|
|
43
|
+
except Exception as e:
|
|
44
|
+
raise Exception(
|
|
45
|
+
"Check `lark-mcp` command failed. Please install it manually."
|
|
46
|
+
) from e
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
check_env()
|
|
50
|
+
|
|
51
|
+
lark_tools = MCPToolset(
|
|
52
|
+
connection_params=StdioServerParameters(
|
|
53
|
+
command="npx",
|
|
54
|
+
args=[
|
|
55
|
+
"-y",
|
|
56
|
+
"@larksuiteoapi/lark-mcp",
|
|
57
|
+
"mcp",
|
|
58
|
+
"-a",
|
|
59
|
+
getenv("TOOL_LARK_ENDPOINT"),
|
|
60
|
+
"-s",
|
|
61
|
+
getenv("TOOL_LARK_API_KEY"),
|
|
62
|
+
"-u",
|
|
63
|
+
getenv("TOOL_LARK_TOKEN"),
|
|
64
|
+
],
|
|
65
|
+
errlog=None,
|
|
66
|
+
),
|
|
67
|
+
)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset, SseConnectionParams
|
|
16
|
+
|
|
17
|
+
from veadk.config import getenv
|
|
18
|
+
|
|
19
|
+
url = getenv("TOOL_LAS_URL")
|
|
20
|
+
dataset_id = getenv("TOOL_LAS_DATASET_ID")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
las = MCPToolset(connection_params=SseConnectionParams(url=url))
|
|
@@ -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
|
|
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 = getenv("TOOL_VESEARCH_API_KEY")
|
|
30
|
+
bot_id = str(getenv("TOOL_VESEARCH_ENDPOINT"))
|
|
31
|
+
|
|
32
|
+
if api_key == "":
|
|
33
|
+
return "Invoke `vesearch` failed. Please set VESEARCH_API_KEY as your environment variable."
|
|
34
|
+
if bot_id == "":
|
|
35
|
+
return "Invoke `vesearch` failed. Please set VESEARCH_BOT_ID 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,76 @@
|
|
|
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
|
+
import requests
|
|
18
|
+
|
|
19
|
+
from veadk.config import getenv
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def web_scraper(query: str) -> dict[str, Any]:
|
|
23
|
+
"""query a single keyword from some search engineer
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
query (str, optional): query keyword. Defaults to None.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
dict[str, Any]: search results
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
endpoint = getenv("TOOL_WEB_SCRAPER_ENDPOINT")
|
|
33
|
+
token = getenv("TOOL_WEB_SCRAPER_API_KEY")
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
url = f"https://{endpoint}/v1/queries"
|
|
37
|
+
headers = {
|
|
38
|
+
"Content-Type": "application/json",
|
|
39
|
+
"X-VE-Source": "google_search",
|
|
40
|
+
"X-VE-API-Key": token,
|
|
41
|
+
}
|
|
42
|
+
data = {
|
|
43
|
+
"query": query,
|
|
44
|
+
"source": "google_search",
|
|
45
|
+
"parse": True,
|
|
46
|
+
"limit": "10",
|
|
47
|
+
"start_page": "1",
|
|
48
|
+
"pages": "1",
|
|
49
|
+
"context": [
|
|
50
|
+
{"key": "nfpr", "value": True},
|
|
51
|
+
{"key": "safe_search", "value": False},
|
|
52
|
+
{"key": "filter", "value": 1},
|
|
53
|
+
],
|
|
54
|
+
}
|
|
55
|
+
response = requests.post(url, headers=headers, json=data, verify=False)
|
|
56
|
+
|
|
57
|
+
response.raise_for_status()
|
|
58
|
+
|
|
59
|
+
response_json = response.json() if response.content else None
|
|
60
|
+
results_dict = (
|
|
61
|
+
response_json.get("results")[0].get("content").get("results").get("organic")
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
results_str = ""
|
|
65
|
+
for r in results_dict:
|
|
66
|
+
dic = {
|
|
67
|
+
"url": r.get("url"),
|
|
68
|
+
"title": r.get("title"),
|
|
69
|
+
"description": r.get("desc"),
|
|
70
|
+
}
|
|
71
|
+
results_str += str(dic)
|
|
72
|
+
return results_str
|
|
73
|
+
|
|
74
|
+
except requests.exceptions.RequestException as e:
|
|
75
|
+
error_message = f"Error: {str(e)}"
|
|
76
|
+
raise ValueError(error_message)
|
|
@@ -0,0 +1,192 @@
|
|
|
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
|
+
"""
|
|
16
|
+
The document of this tool see: https://www.volcengine.com/docs/85508/1650263
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import datetime
|
|
20
|
+
import hashlib
|
|
21
|
+
import hmac
|
|
22
|
+
import json
|
|
23
|
+
from urllib.parse import quote
|
|
24
|
+
|
|
25
|
+
import requests
|
|
26
|
+
|
|
27
|
+
from veadk.config import getenv
|
|
28
|
+
from veadk.utils.logger import get_logger
|
|
29
|
+
|
|
30
|
+
logger = get_logger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
Service = "volc_torchlight_api"
|
|
34
|
+
Version = "2025-01-01"
|
|
35
|
+
Region = "cn-beijing"
|
|
36
|
+
Host = "mercury.volcengineapi.com"
|
|
37
|
+
ContentType = "application/json"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def norm_query(params):
|
|
41
|
+
query = ""
|
|
42
|
+
for key in sorted(params.keys()):
|
|
43
|
+
if isinstance(params[key], list):
|
|
44
|
+
for k in params[key]:
|
|
45
|
+
query = (
|
|
46
|
+
query + quote(key, safe="-_.~") + "=" + quote(k, safe="-_.~") + "&"
|
|
47
|
+
)
|
|
48
|
+
else:
|
|
49
|
+
query = (
|
|
50
|
+
query
|
|
51
|
+
+ quote(key, safe="-_.~")
|
|
52
|
+
+ "="
|
|
53
|
+
+ quote(params[key], safe="-_.~")
|
|
54
|
+
+ "&"
|
|
55
|
+
)
|
|
56
|
+
query = query[:-1]
|
|
57
|
+
return query.replace("+", "%20")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def hmac_sha256(key: bytes, content: str):
|
|
61
|
+
return hmac.new(key, content.encode("utf-8"), hashlib.sha256).digest()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def hash_sha256(content: str):
|
|
65
|
+
return hashlib.sha256(content.encode("utf-8")).hexdigest()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def request(method, date, query, header, ak, sk, action, body):
|
|
69
|
+
credential = {
|
|
70
|
+
"access_key_id": ak,
|
|
71
|
+
"secret_access_key": sk,
|
|
72
|
+
"service": Service,
|
|
73
|
+
"region": Region,
|
|
74
|
+
}
|
|
75
|
+
request_param = {
|
|
76
|
+
"body": body,
|
|
77
|
+
"host": Host,
|
|
78
|
+
"path": "/",
|
|
79
|
+
"method": method,
|
|
80
|
+
"content_type": ContentType,
|
|
81
|
+
"date": date,
|
|
82
|
+
"query": {"Action": action, "Version": Version, **query},
|
|
83
|
+
}
|
|
84
|
+
if body is None:
|
|
85
|
+
request_param["body"] = ""
|
|
86
|
+
# 第四步:接下来开始计算签名。在计算签名前,先准备好用于接收签算结果的 signResult 变量,并设置一些参数。
|
|
87
|
+
# 初始化签名结果的结构体
|
|
88
|
+
x_date = request_param["date"].strftime("%Y%m%dT%H%M%SZ")
|
|
89
|
+
short_x_date = x_date[:8]
|
|
90
|
+
x_content_sha256 = hash_sha256(request_param["body"])
|
|
91
|
+
sign_result = {
|
|
92
|
+
"Host": request_param["host"],
|
|
93
|
+
"X-Content-Sha256": x_content_sha256,
|
|
94
|
+
"X-Date": x_date,
|
|
95
|
+
"Content-Type": request_param["content_type"],
|
|
96
|
+
}
|
|
97
|
+
signed_headers_str = ";".join(
|
|
98
|
+
["content-type", "host", "x-content-sha256", "x-date"]
|
|
99
|
+
)
|
|
100
|
+
# signed_headers_str = signed_headers_str + ";x-security-token"
|
|
101
|
+
canonical_request_str = "\n".join(
|
|
102
|
+
[
|
|
103
|
+
request_param["method"].upper(),
|
|
104
|
+
request_param["path"],
|
|
105
|
+
norm_query(request_param["query"]),
|
|
106
|
+
"\n".join(
|
|
107
|
+
[
|
|
108
|
+
"content-type:" + request_param["content_type"],
|
|
109
|
+
"host:" + request_param["host"],
|
|
110
|
+
"x-content-sha256:" + x_content_sha256,
|
|
111
|
+
"x-date:" + x_date,
|
|
112
|
+
]
|
|
113
|
+
),
|
|
114
|
+
"",
|
|
115
|
+
signed_headers_str,
|
|
116
|
+
x_content_sha256,
|
|
117
|
+
]
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
hashed_canonical_request = hash_sha256(canonical_request_str)
|
|
121
|
+
|
|
122
|
+
credential_scope = "/".join(
|
|
123
|
+
[short_x_date, credential["region"], credential["service"], "request"]
|
|
124
|
+
)
|
|
125
|
+
string_to_sign = "\n".join(
|
|
126
|
+
["HMAC-SHA256", x_date, credential_scope, hashed_canonical_request]
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
k_date = hmac_sha256(credential["secret_access_key"].encode("utf-8"), short_x_date)
|
|
130
|
+
k_region = hmac_sha256(k_date, credential["region"])
|
|
131
|
+
k_service = hmac_sha256(k_region, credential["service"])
|
|
132
|
+
k_signing = hmac_sha256(k_service, "request")
|
|
133
|
+
signature = hmac_sha256(k_signing, string_to_sign).hex()
|
|
134
|
+
|
|
135
|
+
sign_result["Authorization"] = (
|
|
136
|
+
"HMAC-SHA256 Credential={}, SignedHeaders={}, Signature={}".format(
|
|
137
|
+
credential["access_key_id"] + "/" + credential_scope,
|
|
138
|
+
signed_headers_str,
|
|
139
|
+
signature,
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
header = {**header, **sign_result}
|
|
143
|
+
# header = {**header, **{"X-Security-Token": SessionToken}}
|
|
144
|
+
r = requests.request(
|
|
145
|
+
method=method,
|
|
146
|
+
url="https://{}{}".format(request_param["host"], request_param["path"]),
|
|
147
|
+
headers=header,
|
|
148
|
+
params=request_param["query"],
|
|
149
|
+
data=request_param["body"],
|
|
150
|
+
)
|
|
151
|
+
return r.json()
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def web_search(query: str) -> list[str]:
|
|
155
|
+
"""Search a query in websites.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
query: The query to search.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
A list of result documents.
|
|
162
|
+
"""
|
|
163
|
+
req = {
|
|
164
|
+
"Query": query,
|
|
165
|
+
"SearchType": "web",
|
|
166
|
+
"Count": 5,
|
|
167
|
+
"NeedSummary": True,
|
|
168
|
+
}
|
|
169
|
+
ak = getenv("VOLCENGINE_ACCESS_KEY")
|
|
170
|
+
sk = getenv("VOLCENGINE_SECRET_KEY")
|
|
171
|
+
|
|
172
|
+
now = datetime.datetime.utcnow()
|
|
173
|
+
response_body = request(
|
|
174
|
+
"POST",
|
|
175
|
+
now,
|
|
176
|
+
{},
|
|
177
|
+
{},
|
|
178
|
+
ak,
|
|
179
|
+
sk,
|
|
180
|
+
"WebSearch",
|
|
181
|
+
json.dumps(req),
|
|
182
|
+
)
|
|
183
|
+
try:
|
|
184
|
+
results: list = response_body["Result"]["WebResults"]
|
|
185
|
+
except Exception as e:
|
|
186
|
+
logger.error(f"Web search failed: {e}")
|
|
187
|
+
return []
|
|
188
|
+
|
|
189
|
+
final_results = []
|
|
190
|
+
for result in results:
|
|
191
|
+
final_results.append(result["Summary"].strip())
|
|
192
|
+
return final_results
|