veadk-python 0.2.2__py3-none-any.whl → 0.2.5__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/agent.py +31 -21
- veadk/agents/loop_agent.py +55 -0
- veadk/agents/parallel_agent.py +60 -0
- veadk/agents/sequential_agent.py +55 -0
- veadk/cli/cli_deploy.py +14 -1
- veadk/cli/cli_web.py +27 -0
- veadk/cloud/cloud_app.py +21 -6
- veadk/consts.py +14 -1
- veadk/database/viking/viking_database.py +3 -3
- veadk/evaluation/adk_evaluator/__init__.py +4 -0
- veadk/evaluation/adk_evaluator/adk_evaluator.py +170 -217
- veadk/evaluation/base_evaluator.py +26 -20
- veadk/evaluation/deepeval_evaluator/deepeval_evaluator.py +8 -5
- veadk/{tracing/telemetry/metrics/__init__.py → integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/clean.py} +10 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/app.py +40 -7
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/run.sh +11 -5
- veadk/integrations/ve_faas/ve_faas.py +5 -1
- veadk/integrations/ve_tos/ve_tos.py +176 -0
- veadk/runner.py +162 -39
- veadk/tools/builtin_tools/image_edit.py +236 -0
- veadk/tools/builtin_tools/image_generate.py +236 -0
- veadk/tools/builtin_tools/video_generate.py +326 -0
- veadk/tools/sandbox/browser_sandbox.py +19 -9
- veadk/tools/sandbox/code_sandbox.py +21 -11
- veadk/tools/sandbox/computer_sandbox.py +16 -9
- veadk/tracing/base_tracer.py +6 -200
- veadk/tracing/telemetry/attributes/attributes.py +29 -0
- veadk/tracing/telemetry/attributes/extractors/common_attributes_extractors.py +71 -0
- veadk/tracing/telemetry/attributes/extractors/llm_attributes_extractors.py +451 -0
- veadk/tracing/telemetry/attributes/extractors/tool_attributes_extractors.py +76 -0
- veadk/tracing/telemetry/attributes/extractors/types.py +75 -0
- veadk/tracing/telemetry/exporters/apmplus_exporter.py +97 -38
- veadk/tracing/telemetry/exporters/base_exporter.py +10 -10
- veadk/tracing/telemetry/exporters/cozeloop_exporter.py +20 -13
- veadk/tracing/telemetry/exporters/inmemory_exporter.py +49 -32
- veadk/tracing/telemetry/exporters/tls_exporter.py +18 -12
- veadk/tracing/telemetry/opentelemetry_tracer.py +105 -102
- veadk/tracing/telemetry/telemetry.py +238 -0
- veadk/types.py +6 -1
- veadk/utils/misc.py +41 -1
- veadk/utils/patches.py +25 -0
- veadk/version.py +1 -1
- veadk_python-0.2.5.dist-info/METADATA +345 -0
- veadk_python-0.2.5.dist-info/RECORD +127 -0
- veadk/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/__pycache__/agent.cpython-310.pyc +0 -0
- veadk/__pycache__/config.cpython-310.pyc +0 -0
- veadk/__pycache__/consts.cpython-310.pyc +0 -0
- veadk/__pycache__/runner.cpython-310.pyc +0 -0
- veadk/__pycache__/types.cpython-310.pyc +0 -0
- veadk/__pycache__/version.cpython-310.pyc +0 -0
- veadk/a2a/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/a2a/__pycache__/agent_card.cpython-310.pyc +0 -0
- veadk/a2a/__pycache__/remote_ve_agent.cpython-310.pyc +0 -0
- veadk/a2a/__pycache__/ve_a2a_server.cpython-310.pyc +0 -0
- veadk/a2a/__pycache__/ve_agent_executor.cpython-310.pyc +0 -0
- veadk/cli/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/cli/__pycache__/cli.cpython-310.pyc +0 -0
- veadk/cli/__pycache__/cli_deploy.cpython-310.pyc +0 -0
- veadk/cli/__pycache__/cli_init.cpython-310.pyc +0 -0
- veadk/cli/__pycache__/cli_prompt.cpython-310.pyc +0 -0
- veadk/cli/__pycache__/cli_studio.cpython-310.pyc +0 -0
- veadk/cli/__pycache__/cli_web.cpython-310.pyc +0 -0
- veadk/cli/__pycache__/main.cpython-310.pyc +0 -0
- veadk/cloud/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/cloud/__pycache__/cloud_agent_engine.cpython-310.pyc +0 -0
- veadk/cloud/__pycache__/cloud_app.cpython-310.pyc +0 -0
- veadk/database/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/database/__pycache__/base_database.cpython-310.pyc +0 -0
- veadk/database/__pycache__/database_adapter.cpython-310.pyc +0 -0
- veadk/database/__pycache__/database_factory.cpython-310.pyc +0 -0
- veadk/database/__pycache__/local_database.cpython-310.pyc +0 -0
- veadk/database/kv/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/database/relational/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/database/vector/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/database/vector/__pycache__/opensearch_vector_database.cpython-310.pyc +0 -0
- veadk/database/vector/__pycache__/type.cpython-310.pyc +0 -0
- veadk/database/viking/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/evaluation/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/evaluation/__pycache__/base_evaluator.cpython-310.pyc +0 -0
- veadk/evaluation/__pycache__/eval_set_file_loader.cpython-310.pyc +0 -0
- veadk/evaluation/__pycache__/eval_set_recorder.cpython-310.pyc +0 -0
- veadk/evaluation/__pycache__/types.cpython-310.pyc +0 -0
- veadk/evaluation/adk_evaluator/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/evaluation/deepeval_evaluator/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/evaluation/deepeval_evaluator/__pycache__/deepeval_evaluator.cpython-310.pyc +0 -0
- veadk/evaluation/utils/__pycache__/prometheus.cpython-310.pyc +0 -0
- veadk/integrations/ve_apig/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/integrations/ve_apig/__pycache__/apig.cpython-310.pyc +0 -0
- veadk/integrations/ve_apig/__pycache__/ve_apig.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/__pycache__/types.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/__pycache__/ve_faas.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/__pycache__/ve_faas_utils.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/__pycache__/vefaas.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/__pycache__/vefaas_utils.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__pycache__/agent.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__pycache__/app.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__pycache__/studio_app.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name|replace('-', '_') }}/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name|replace('-', '_') }}/__pycache__/agent.cpython-310.pyc +0 -0
- veadk/integrations/ve_prompt_pilot/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/integrations/ve_prompt_pilot/__pycache__/agentpilot.cpython-310.pyc +0 -0
- veadk/knowledgebase/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/knowledgebase/__pycache__/knowledgebase.cpython-310.pyc +0 -0
- veadk/knowledgebase/__pycache__/knowledgebase_database_adapter.cpython-310.pyc +0 -0
- veadk/memory/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/memory/__pycache__/long_term_memory.cpython-310.pyc +0 -0
- veadk/memory/__pycache__/memory_database_adapter.cpython-310.pyc +0 -0
- veadk/memory/__pycache__/short_term_memory.cpython-310.pyc +0 -0
- veadk/memory/__pycache__/short_term_memory_processor.cpython-310.pyc +0 -0
- veadk/prompts/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/prompts/__pycache__/agent_default_prompt.cpython-310.pyc +0 -0
- veadk/prompts/__pycache__/prompt_memory_processor.cpython-310.pyc +0 -0
- veadk/prompts/__pycache__/prompt_optimization.cpython-310.pyc +0 -0
- veadk/tools/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/tools/__pycache__/demo_tools.cpython-310.pyc +0 -0
- veadk/tools/__pycache__/load_knowledgebase_tool.cpython-310.pyc +0 -0
- veadk/tools/builtin_tools/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/tools/builtin_tools/__pycache__/lark.cpython-310.pyc +0 -0
- veadk/tools/builtin_tools/__pycache__/vesearch.cpython-310.pyc +0 -0
- veadk/tools/builtin_tools/__pycache__/web_search.cpython-310.pyc +0 -0
- veadk/tools/sandbox/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/tracing/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/tracing/__pycache__/base_tracer.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/__pycache__/opentelemetry_tracer.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/exporters/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/exporters/__pycache__/apiserver_exporter.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/exporters/__pycache__/apmplus_exporter.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/exporters/__pycache__/base_exporter.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/exporters/__pycache__/cozeloop_exporter.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/exporters/__pycache__/inmemory_exporter.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/exporters/__pycache__/tls_exporter.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/metrics/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/metrics/__pycache__/opentelemetry_metrics.cpython-310.pyc +0 -0
- veadk/tracing/telemetry/metrics/opentelemetry_metrics.py +0 -73
- veadk/utils/__pycache__/__init__.cpython-310.pyc +0 -0
- veadk/utils/__pycache__/logger.cpython-310.pyc +0 -0
- veadk/utils/__pycache__/mcp_utils.cpython-310.pyc +0 -0
- veadk/utils/__pycache__/misc.cpython-310.pyc +0 -0
- veadk/utils/__pycache__/patches.cpython-310.pyc +0 -0
- veadk/utils/__pycache__/volcengine_sign.cpython-310.pyc +0 -0
- veadk_python-0.2.2.dist-info/METADATA +0 -144
- veadk_python-0.2.2.dist-info/RECORD +0 -213
- {veadk_python-0.2.2.dist-info → veadk_python-0.2.5.dist-info}/WHEEL +0 -0
- {veadk_python-0.2.2.dist-info → veadk_python-0.2.5.dist-info}/entry_points.txt +0 -0
- {veadk_python-0.2.2.dist-info → veadk_python-0.2.5.dist-info}/licenses/LICENSE +0 -0
- {veadk_python-0.2.2.dist-info → veadk_python-0.2.5.dist-info}/top_level.txt +0 -0
|
@@ -17,9 +17,17 @@ from contextlib import asynccontextmanager
|
|
|
17
17
|
from typing import Callable
|
|
18
18
|
|
|
19
19
|
from agent import agent_run_config
|
|
20
|
+
|
|
20
21
|
from fastapi import FastAPI
|
|
22
|
+
from fastapi.routing import APIRoute
|
|
23
|
+
|
|
21
24
|
from fastmcp import FastMCP
|
|
22
25
|
|
|
26
|
+
from starlette.routing import Route
|
|
27
|
+
|
|
28
|
+
from google.adk.a2a.utils.agent_card_builder import AgentCardBuilder
|
|
29
|
+
from a2a.types import AgentProvider
|
|
30
|
+
|
|
23
31
|
from veadk.a2a.ve_a2a_server import init_app
|
|
24
32
|
from veadk.runner import Runner
|
|
25
33
|
from veadk.tracing.telemetry.exporters.apmplus_exporter import APMPlusExporter
|
|
@@ -39,6 +47,10 @@ app_name = agent_run_config.app_name
|
|
|
39
47
|
agent = agent_run_config.agent
|
|
40
48
|
short_term_memory = agent_run_config.short_term_memory
|
|
41
49
|
|
|
50
|
+
VEFAAS_REGION = os.getenv("APP_REGION", "cn-beijing")
|
|
51
|
+
VEFAAS_FUNC_ID = os.getenv("_FAAS_FUNC_ID", "")
|
|
52
|
+
agent_card_builder = AgentCardBuilder(agent=agent, provider=AgentProvider(organization="Volcengine Agent Development Kit (VeADK)", url=f"https://console.volcengine.com/vefaas/region:vefaas+{VEFAAS_REGION}/function/detail/{VEFAAS_FUNC_ID}"))
|
|
53
|
+
|
|
42
54
|
|
|
43
55
|
def load_tracer() -> None:
|
|
44
56
|
EXPORTER_REGISTRY = {
|
|
@@ -61,11 +73,8 @@ def load_tracer() -> None:
|
|
|
61
73
|
else:
|
|
62
74
|
exporters.append(exporter_cls())
|
|
63
75
|
|
|
64
|
-
tracer = OpentelemetryTracer(
|
|
65
|
-
name="veadk_tracer", app_name=agent_run_config.app_name, exporters=exporters
|
|
66
|
-
)
|
|
76
|
+
tracer = OpentelemetryTracer(name="veadk_tracer", exporters=exporters)
|
|
67
77
|
agent_run_config.agent.tracers.extend([tracer])
|
|
68
|
-
tracer.do_hooks(agent=agent_run_config.agent)
|
|
69
78
|
|
|
70
79
|
|
|
71
80
|
def build_mcp_run_agent_func() -> Callable:
|
|
@@ -104,8 +113,16 @@ def build_mcp_run_agent_func() -> Callable:
|
|
|
104
113
|
return run_agent
|
|
105
114
|
|
|
106
115
|
|
|
116
|
+
async def agent_card() -> dict:
|
|
117
|
+
agent_card = await agent_card_builder.build()
|
|
118
|
+
return agent_card.model_dump()
|
|
119
|
+
|
|
120
|
+
|
|
107
121
|
load_tracer()
|
|
108
122
|
|
|
123
|
+
# Build a run_agent function for building MCP server
|
|
124
|
+
run_agent_func = build_mcp_run_agent_func()
|
|
125
|
+
|
|
109
126
|
a2a_app = init_app(
|
|
110
127
|
server_url="0.0.0.0",
|
|
111
128
|
app_name=app_name,
|
|
@@ -113,9 +130,8 @@ a2a_app = init_app(
|
|
|
113
130
|
short_term_memory=short_term_memory,
|
|
114
131
|
)
|
|
115
132
|
|
|
116
|
-
# Build a run_agent function for building MCP server
|
|
117
|
-
run_agent_func = build_mcp_run_agent_func()
|
|
118
133
|
a2a_app.post("/run_agent", operation_id="run_agent", tags=["mcp"])(run_agent_func)
|
|
134
|
+
a2a_app.get("/agent_card", operation_id="agent_card", tags=["mcp"])(agent_card)
|
|
119
135
|
|
|
120
136
|
|
|
121
137
|
# === Build mcp server ===
|
|
@@ -134,7 +150,14 @@ async def combined_lifespan(app: FastAPI):
|
|
|
134
150
|
|
|
135
151
|
|
|
136
152
|
# Create main FastAPI app with combined lifespan
|
|
137
|
-
app = FastAPI(
|
|
153
|
+
app = FastAPI(
|
|
154
|
+
title=a2a_app.title,
|
|
155
|
+
version=a2a_app.version,
|
|
156
|
+
lifespan=combined_lifespan,
|
|
157
|
+
openapi_url=None,
|
|
158
|
+
docs_url=None,
|
|
159
|
+
redoc_url=None
|
|
160
|
+
)
|
|
138
161
|
|
|
139
162
|
# Mount A2A routes to main app
|
|
140
163
|
for route in a2a_app.routes:
|
|
@@ -143,4 +166,14 @@ for route in a2a_app.routes:
|
|
|
143
166
|
# Mount MCP server at /mcp endpoint
|
|
144
167
|
app.mount("/mcp", mcp_app)
|
|
145
168
|
|
|
169
|
+
|
|
170
|
+
# remove openapi routes
|
|
171
|
+
paths = ["/openapi.json", "/docs", "/redoc"]
|
|
172
|
+
new_routes = []
|
|
173
|
+
for route in app.router.routes:
|
|
174
|
+
if isinstance(route, (APIRoute, Route)) and route.path in paths:
|
|
175
|
+
continue
|
|
176
|
+
new_routes.append(route)
|
|
177
|
+
app.router.routes = new_routes
|
|
178
|
+
|
|
146
179
|
# === Build mcp server end ===
|
|
@@ -33,12 +33,18 @@ while [[ $# -gt 0 ]]; do
|
|
|
33
33
|
esac
|
|
34
34
|
done
|
|
35
35
|
|
|
36
|
-
# in case of
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
# in case of deployment deps not installed in user's requirements.txt
|
|
37
|
+
if pip list | grep -q "^fastapi \|^uvicorn "; then
|
|
38
|
+
echo "fastapi and uvicorn already installed"
|
|
39
|
+
else
|
|
40
|
+
python3 -m pip install uvicorn[standard] fastapi
|
|
41
|
+
fi
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
# Check if MODEL_AGENT_API_KEY is set
|
|
44
|
+
if [ -z "$MODEL_AGENT_API_KEY" ]; then
|
|
45
|
+
echo "MODEL_AGENT_API_KEY is not set. Please set it in your environment variables."
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
42
48
|
|
|
43
49
|
USE_ADK_WEB=${USE_ADK_WEB:-False}
|
|
44
50
|
|
|
@@ -120,7 +120,11 @@ class VeFaaS:
|
|
|
120
120
|
envs=envs,
|
|
121
121
|
)
|
|
122
122
|
)
|
|
123
|
-
|
|
123
|
+
|
|
124
|
+
# avoid print secrets
|
|
125
|
+
logger.debug(
|
|
126
|
+
f"Function creation in {res.project_name} project with ID {res.id}"
|
|
127
|
+
)
|
|
124
128
|
|
|
125
129
|
function_id = res.id
|
|
126
130
|
|
|
@@ -0,0 +1,176 @@
|
|
|
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
|
+
from veadk.config import getenv
|
|
17
|
+
from veadk.utils.logger import get_logger
|
|
18
|
+
import tos
|
|
19
|
+
import asyncio
|
|
20
|
+
from typing import Union
|
|
21
|
+
from pydantic import BaseModel, Field
|
|
22
|
+
from typing import Any
|
|
23
|
+
from urllib.parse import urlparse
|
|
24
|
+
from datetime import datetime
|
|
25
|
+
|
|
26
|
+
logger = get_logger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class TOSConfig(BaseModel):
|
|
30
|
+
region: str = Field(
|
|
31
|
+
default_factory=lambda: getenv("DATABASE_TOS_REGION"),
|
|
32
|
+
description="TOS region",
|
|
33
|
+
)
|
|
34
|
+
ak: str = Field(
|
|
35
|
+
default_factory=lambda: getenv("VOLCENGINE_ACCESS_KEY"),
|
|
36
|
+
description="Volcengine access key",
|
|
37
|
+
)
|
|
38
|
+
sk: str = Field(
|
|
39
|
+
default_factory=lambda: getenv("VOLCENGINE_SECRET_KEY"),
|
|
40
|
+
description="Volcengine secret key",
|
|
41
|
+
)
|
|
42
|
+
bucket_name: str = Field(
|
|
43
|
+
default_factory=lambda: getenv("DATABASE_TOS_BUCKET"),
|
|
44
|
+
description="TOS bucket name",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class VeTOS(BaseModel):
|
|
49
|
+
config: TOSConfig = Field(default_factory=TOSConfig)
|
|
50
|
+
|
|
51
|
+
def model_post_init(self, __context: Any) -> None:
|
|
52
|
+
try:
|
|
53
|
+
self._client = tos.TosClientV2(
|
|
54
|
+
self.config.ak,
|
|
55
|
+
self.config.sk,
|
|
56
|
+
endpoint=f"tos-{self.config.region}.volces.com",
|
|
57
|
+
region=self.config.region,
|
|
58
|
+
)
|
|
59
|
+
logger.info("Connected to TOS successfully.")
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logger.error(f"Client initialization failed:{e}")
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
def create_bucket(self) -> bool:
|
|
65
|
+
"""If the bucket does not exist, create it"""
|
|
66
|
+
try:
|
|
67
|
+
self._client.head_bucket(self.config.bucket_name)
|
|
68
|
+
logger.info(f"Bucket {self.config.bucket_name} already exists")
|
|
69
|
+
return True
|
|
70
|
+
except tos.exceptions.TosServerError as e:
|
|
71
|
+
if e.status_code == 404:
|
|
72
|
+
self._client.create_bucket(
|
|
73
|
+
bucket=self.config.bucket_name,
|
|
74
|
+
storage_class=tos.StorageClassType.Storage_Class_Standard,
|
|
75
|
+
acl=tos.ACLType.ACL_Private,
|
|
76
|
+
)
|
|
77
|
+
logger.info(f"Bucket {self.config.bucket_name} created successfully")
|
|
78
|
+
return True
|
|
79
|
+
except Exception as e:
|
|
80
|
+
logger.error(f"Bucket creation failed: {str(e)}")
|
|
81
|
+
return False
|
|
82
|
+
|
|
83
|
+
def build_tos_url(
|
|
84
|
+
self, user_id: str, app_name: str, session_id: str, data_path: str
|
|
85
|
+
) -> tuple[str, str]:
|
|
86
|
+
"""generate TOS object key"""
|
|
87
|
+
parsed_url = urlparse(data_path)
|
|
88
|
+
|
|
89
|
+
if parsed_url.scheme and parsed_url.scheme in ("http", "https", "ftp", "ftps"):
|
|
90
|
+
file_name = os.path.basename(parsed_url.path)
|
|
91
|
+
else:
|
|
92
|
+
file_name = os.path.basename(data_path)
|
|
93
|
+
|
|
94
|
+
timestamp: str = datetime.now().strftime("%Y%m%d%H%M%S%f")[:-3]
|
|
95
|
+
object_key: str = f"{app_name}-{user_id}-{session_id}/{timestamp}-{file_name}"
|
|
96
|
+
tos_url: str = f"https://{self.config.bucket_name}.tos-{self.config.region}.volces.com/{object_key}"
|
|
97
|
+
|
|
98
|
+
return object_key, tos_url
|
|
99
|
+
|
|
100
|
+
def upload(
|
|
101
|
+
self,
|
|
102
|
+
object_key: str,
|
|
103
|
+
data: Union[str, bytes],
|
|
104
|
+
):
|
|
105
|
+
if isinstance(data, str):
|
|
106
|
+
data_type = "file"
|
|
107
|
+
elif isinstance(data, bytes):
|
|
108
|
+
data_type = "bytes"
|
|
109
|
+
else:
|
|
110
|
+
error_msg = f"Upload failed: data type error. Only str (file path) and bytes are supported, got {type(data)}"
|
|
111
|
+
logger.error(error_msg)
|
|
112
|
+
raise ValueError(error_msg)
|
|
113
|
+
if data_type == "file":
|
|
114
|
+
return asyncio.to_thread(self._do_upload_file, object_key, data)
|
|
115
|
+
elif data_type == "bytes":
|
|
116
|
+
return asyncio.to_thread(self._do_upload_bytes, object_key, data)
|
|
117
|
+
|
|
118
|
+
def _do_upload_bytes(self, object_key: str, bytes: bytes) -> bool:
|
|
119
|
+
try:
|
|
120
|
+
if not self._client:
|
|
121
|
+
return False
|
|
122
|
+
if not self.create_bucket():
|
|
123
|
+
return False
|
|
124
|
+
self._client.put_object(
|
|
125
|
+
bucket=self.config.bucket_name, key=object_key, content=bytes
|
|
126
|
+
)
|
|
127
|
+
logger.debug(f"Upload success, object_key: {object_key}")
|
|
128
|
+
self._close()
|
|
129
|
+
return True
|
|
130
|
+
except Exception as e:
|
|
131
|
+
logger.error(f"Upload failed: {e}")
|
|
132
|
+
self._close()
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
def _do_upload_file(self, object_key: str, file_path: str) -> bool:
|
|
136
|
+
try:
|
|
137
|
+
if not self._client:
|
|
138
|
+
return False
|
|
139
|
+
if not self.create_bucket():
|
|
140
|
+
return False
|
|
141
|
+
|
|
142
|
+
self._client.put_object_from_file(
|
|
143
|
+
bucket=self.config.bucket_name, key=object_key, file_path=file_path
|
|
144
|
+
)
|
|
145
|
+
self._close()
|
|
146
|
+
logger.debug(f"Upload success, object_key: {object_key}")
|
|
147
|
+
return True
|
|
148
|
+
except Exception as e:
|
|
149
|
+
logger.error(f"Upload failed: {e}")
|
|
150
|
+
self._close()
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
def download(self, object_key: str, save_path: str) -> bool:
|
|
154
|
+
"""download image from TOS"""
|
|
155
|
+
try:
|
|
156
|
+
object_stream = self._client.get_object(self.config.bucket_name, object_key)
|
|
157
|
+
|
|
158
|
+
save_dir = os.path.dirname(save_path)
|
|
159
|
+
if save_dir and not os.path.exists(save_dir):
|
|
160
|
+
os.makedirs(save_dir, exist_ok=True)
|
|
161
|
+
|
|
162
|
+
with open(save_path, "wb") as f:
|
|
163
|
+
for chunk in object_stream:
|
|
164
|
+
f.write(chunk)
|
|
165
|
+
|
|
166
|
+
logger.debug(f"Image download success, saved to: {save_path}")
|
|
167
|
+
return True
|
|
168
|
+
|
|
169
|
+
except Exception as e:
|
|
170
|
+
logger.error(f"Image download failed: {str(e)}")
|
|
171
|
+
|
|
172
|
+
return False
|
|
173
|
+
|
|
174
|
+
def _close(self):
|
|
175
|
+
if self._client:
|
|
176
|
+
self._client.close()
|
veadk/runner.py
CHANGED
|
@@ -11,21 +11,28 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
+
import asyncio
|
|
14
15
|
from typing import Union
|
|
15
16
|
|
|
16
17
|
from google.adk.agents import RunConfig
|
|
18
|
+
from google.adk.agents.invocation_context import LlmCallsLimitExceededError
|
|
17
19
|
from google.adk.agents.run_config import StreamingMode
|
|
20
|
+
from google.adk.plugins.base_plugin import BasePlugin
|
|
18
21
|
from google.adk.runners import Runner as ADKRunner
|
|
19
22
|
from google.genai import types
|
|
20
23
|
from google.genai.types import Blob
|
|
21
24
|
|
|
22
25
|
from veadk.a2a.remote_ve_agent import RemoteVeAgent
|
|
23
26
|
from veadk.agent import Agent
|
|
27
|
+
from veadk.agents.loop_agent import LoopAgent
|
|
28
|
+
from veadk.agents.parallel_agent import ParallelAgent
|
|
29
|
+
from veadk.agents.sequential_agent import SequentialAgent
|
|
24
30
|
from veadk.evaluation import EvalSetRecorder
|
|
25
31
|
from veadk.memory.short_term_memory import ShortTermMemory
|
|
26
32
|
from veadk.types import MediaMessage
|
|
27
33
|
from veadk.utils.logger import get_logger
|
|
28
34
|
from veadk.utils.misc import read_png_to_bytes
|
|
35
|
+
from veadk.integrations.ve_tos.ve_tos import VeTOS
|
|
29
36
|
|
|
30
37
|
logger = get_logger(__name__)
|
|
31
38
|
|
|
@@ -38,30 +45,36 @@ RunnerMessage = Union[
|
|
|
38
45
|
list[MediaMessage | str], # multiple turn prompt with media and text-based prompt
|
|
39
46
|
]
|
|
40
47
|
|
|
48
|
+
VeAgent = Union[Agent, RemoteVeAgent, SequentialAgent, ParallelAgent, LoopAgent]
|
|
49
|
+
|
|
41
50
|
|
|
42
51
|
class Runner:
|
|
43
52
|
def __init__(
|
|
44
53
|
self,
|
|
45
|
-
agent:
|
|
46
|
-
short_term_memory: ShortTermMemory,
|
|
54
|
+
agent: VeAgent,
|
|
55
|
+
short_term_memory: ShortTermMemory | None = None,
|
|
56
|
+
plugins: list[BasePlugin] | None = None,
|
|
47
57
|
app_name: str = "veadk_default_app",
|
|
48
58
|
user_id: str = "veadk_default_user",
|
|
49
59
|
):
|
|
50
|
-
# basic settings
|
|
51
60
|
self.app_name = app_name
|
|
52
61
|
self.user_id = user_id
|
|
53
62
|
|
|
54
|
-
# agent settings
|
|
55
63
|
self.agent = agent
|
|
56
64
|
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
if not short_term_memory:
|
|
66
|
+
logger.info(
|
|
67
|
+
"No short term memory provided, using a in-memory memory by default."
|
|
68
|
+
)
|
|
69
|
+
self.short_term_memory = ShortTermMemory()
|
|
70
|
+
else:
|
|
71
|
+
self.short_term_memory = short_term_memory
|
|
72
|
+
|
|
73
|
+
self.session_service = self.short_term_memory.session_service
|
|
59
74
|
|
|
60
75
|
# prevent VeRemoteAgent has no long-term memory attr
|
|
61
76
|
if isinstance(self.agent, Agent):
|
|
62
77
|
self.long_term_memory = self.agent.long_term_memory
|
|
63
|
-
for tracer in self.agent.tracers:
|
|
64
|
-
tracer.set_app_name(self.app_name)
|
|
65
78
|
else:
|
|
66
79
|
self.long_term_memory = None
|
|
67
80
|
|
|
@@ -70,15 +83,28 @@ class Runner:
|
|
|
70
83
|
agent=self.agent,
|
|
71
84
|
session_service=self.session_service,
|
|
72
85
|
memory_service=self.long_term_memory,
|
|
86
|
+
plugins=plugins,
|
|
73
87
|
)
|
|
74
88
|
|
|
75
|
-
def _convert_messages(self, messages) -> list:
|
|
89
|
+
def _convert_messages(self, messages, session_id) -> list:
|
|
76
90
|
if isinstance(messages, str):
|
|
77
91
|
messages = [types.Content(role="user", parts=[types.Part(text=messages)])]
|
|
78
92
|
elif isinstance(messages, MediaMessage):
|
|
79
93
|
assert messages.media.endswith(".png"), (
|
|
80
94
|
"The MediaMessage only supports PNG format file for now."
|
|
81
95
|
)
|
|
96
|
+
data = read_png_to_bytes(messages.media)
|
|
97
|
+
|
|
98
|
+
ve_tos = VeTOS()
|
|
99
|
+
object_key, tos_url = ve_tos.build_tos_url(
|
|
100
|
+
self.user_id, self.app_name, session_id, messages.media
|
|
101
|
+
)
|
|
102
|
+
try:
|
|
103
|
+
asyncio.create_task(ve_tos.upload(object_key, data))
|
|
104
|
+
except Exception as e:
|
|
105
|
+
logger.error(f"Upload to TOS failed: {e}")
|
|
106
|
+
tos_url = None
|
|
107
|
+
|
|
82
108
|
messages = [
|
|
83
109
|
types.Content(
|
|
84
110
|
role="user",
|
|
@@ -86,8 +112,8 @@ class Runner:
|
|
|
86
112
|
types.Part(text=messages.text),
|
|
87
113
|
types.Part(
|
|
88
114
|
inline_data=Blob(
|
|
89
|
-
display_name=
|
|
90
|
-
data=
|
|
115
|
+
display_name=tos_url,
|
|
116
|
+
data=data,
|
|
91
117
|
mime_type="image/png",
|
|
92
118
|
)
|
|
93
119
|
),
|
|
@@ -97,7 +123,7 @@ class Runner:
|
|
|
97
123
|
elif isinstance(messages, list):
|
|
98
124
|
converted_messages = []
|
|
99
125
|
for message in messages:
|
|
100
|
-
converted_messages.extend(self._convert_messages(message))
|
|
126
|
+
converted_messages.extend(self._convert_messages(message, session_id))
|
|
101
127
|
messages = converted_messages
|
|
102
128
|
else:
|
|
103
129
|
raise ValueError(f"Unknown message type: {type(messages)}")
|
|
@@ -108,35 +134,44 @@ class Runner:
|
|
|
108
134
|
self,
|
|
109
135
|
session_id: str,
|
|
110
136
|
message: types.Content,
|
|
137
|
+
run_config: RunConfig | None = None,
|
|
111
138
|
stream: bool = False,
|
|
112
139
|
):
|
|
113
140
|
stream_mode = StreamingMode.SSE if stream else StreamingMode.NONE
|
|
114
141
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
run_config=RunConfig(streaming_mode=stream_mode),
|
|
121
|
-
):
|
|
122
|
-
if event.get_function_calls():
|
|
123
|
-
for function_call in event.get_function_calls():
|
|
124
|
-
logger.debug(f"Function call: {function_call}")
|
|
125
|
-
elif (
|
|
126
|
-
event.content is not None
|
|
127
|
-
and event.content.parts
|
|
128
|
-
and event.content.parts[0].text is not None
|
|
129
|
-
and len(event.content.parts[0].text.strip()) > 0
|
|
130
|
-
):
|
|
131
|
-
yield event.content.parts[0].text
|
|
142
|
+
if run_config is not None:
|
|
143
|
+
stream_mode = run_config.streaming_mode
|
|
144
|
+
else:
|
|
145
|
+
run_config = RunConfig(streaming_mode=stream_mode)
|
|
146
|
+
try:
|
|
132
147
|
|
|
133
|
-
|
|
134
|
-
|
|
148
|
+
async def event_generator():
|
|
149
|
+
async for event in self.runner.run_async(
|
|
150
|
+
user_id=self.user_id,
|
|
151
|
+
session_id=session_id,
|
|
152
|
+
new_message=message,
|
|
153
|
+
run_config=run_config,
|
|
154
|
+
):
|
|
155
|
+
if event.get_function_calls():
|
|
156
|
+
for function_call in event.get_function_calls():
|
|
157
|
+
logger.debug(f"Function call: {function_call}")
|
|
158
|
+
elif (
|
|
159
|
+
event.content is not None
|
|
160
|
+
and event.content.parts
|
|
161
|
+
and event.content.parts[0].text is not None
|
|
162
|
+
and len(event.content.parts[0].text.strip()) > 0
|
|
163
|
+
):
|
|
164
|
+
yield event.content.parts[0].text
|
|
165
|
+
|
|
166
|
+
final_output = ""
|
|
167
|
+
async for chunk in event_generator():
|
|
168
|
+
if stream:
|
|
169
|
+
print(chunk, end="", flush=True)
|
|
170
|
+
final_output += chunk
|
|
135
171
|
if stream:
|
|
136
|
-
print(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
print() # end with a new line
|
|
172
|
+
print() # end with a new line
|
|
173
|
+
except LlmCallsLimitExceededError as e:
|
|
174
|
+
logger.warning(f"Max number of llm calls limit exceeded: {e}")
|
|
140
175
|
|
|
141
176
|
return final_output
|
|
142
177
|
|
|
@@ -145,9 +180,10 @@ class Runner:
|
|
|
145
180
|
messages: RunnerMessage,
|
|
146
181
|
session_id: str,
|
|
147
182
|
stream: bool = False,
|
|
183
|
+
run_config: RunConfig | None = None,
|
|
148
184
|
save_tracing_data: bool = False,
|
|
149
185
|
):
|
|
150
|
-
converted_messages: list = self._convert_messages(messages)
|
|
186
|
+
converted_messages: list = self._convert_messages(messages, session_id)
|
|
151
187
|
|
|
152
188
|
await self.short_term_memory.create_session(
|
|
153
189
|
app_name=self.app_name, user_id=self.user_id, session_id=session_id
|
|
@@ -157,19 +193,106 @@ class Runner:
|
|
|
157
193
|
|
|
158
194
|
final_output = ""
|
|
159
195
|
for converted_message in converted_messages:
|
|
160
|
-
final_output = await self._run(
|
|
196
|
+
final_output = await self._run(
|
|
197
|
+
session_id, converted_message, run_config, stream
|
|
198
|
+
)
|
|
161
199
|
|
|
162
200
|
# try to save tracing file
|
|
163
201
|
if save_tracing_data:
|
|
164
202
|
self.save_tracing_file(session_id)
|
|
165
203
|
|
|
204
|
+
self._print_trace_id()
|
|
205
|
+
|
|
166
206
|
return final_output
|
|
167
207
|
|
|
168
|
-
def
|
|
208
|
+
def get_trace_id(self) -> str:
|
|
209
|
+
if not isinstance(self.agent, Agent):
|
|
210
|
+
logger.warning(
|
|
211
|
+
("The agent is not an instance of VeADK Agent, no trace id provided.")
|
|
212
|
+
)
|
|
213
|
+
return "<unknown_trace_id>"
|
|
214
|
+
|
|
215
|
+
if not self.agent.tracers:
|
|
216
|
+
logger.warning(
|
|
217
|
+
"No tracer is configured in the agent, no trace id provided."
|
|
218
|
+
)
|
|
219
|
+
return "<unknown_trace_id>"
|
|
220
|
+
|
|
221
|
+
try:
|
|
222
|
+
trace_id = self.agent.tracers[0].trace_id # type: ignore
|
|
223
|
+
return trace_id
|
|
224
|
+
except Exception as e:
|
|
225
|
+
logger.warning(f"Get tracer id failed as {e}")
|
|
226
|
+
return "<unknown_trace_id>"
|
|
227
|
+
|
|
228
|
+
async def run_with_raw_message(
|
|
229
|
+
self,
|
|
230
|
+
message: types.Content,
|
|
231
|
+
session_id: str,
|
|
232
|
+
run_config: RunConfig | None = None,
|
|
233
|
+
):
|
|
234
|
+
run_config = RunConfig() if not run_config else run_config
|
|
235
|
+
|
|
236
|
+
await self.short_term_memory.create_session(
|
|
237
|
+
app_name=self.app_name, user_id=self.user_id, session_id=session_id
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
try:
|
|
241
|
+
|
|
242
|
+
async def event_generator():
|
|
243
|
+
async for event in self.runner.run_async(
|
|
244
|
+
user_id=self.user_id,
|
|
245
|
+
session_id=session_id,
|
|
246
|
+
new_message=message,
|
|
247
|
+
run_config=run_config,
|
|
248
|
+
):
|
|
249
|
+
if event.get_function_calls():
|
|
250
|
+
for function_call in event.get_function_calls():
|
|
251
|
+
logger.debug(f"Function call: {function_call}")
|
|
252
|
+
elif (
|
|
253
|
+
event.content is not None
|
|
254
|
+
and event.content.parts
|
|
255
|
+
and event.content.parts[0].text is not None
|
|
256
|
+
and len(event.content.parts[0].text.strip()) > 0
|
|
257
|
+
):
|
|
258
|
+
yield event.content.parts[0].text
|
|
259
|
+
|
|
260
|
+
final_output = ""
|
|
261
|
+
|
|
262
|
+
async for chunk in event_generator():
|
|
263
|
+
final_output += chunk
|
|
264
|
+
except LlmCallsLimitExceededError as e:
|
|
265
|
+
logger.warning(f"Max number of llm calls limit exceeded: {e}")
|
|
266
|
+
|
|
267
|
+
return final_output
|
|
268
|
+
|
|
269
|
+
def _print_trace_id(self) -> None:
|
|
169
270
|
if not isinstance(self.agent, Agent):
|
|
271
|
+
logger.warning(
|
|
272
|
+
("The agent is not an instance of VeADK Agent, no trace id provided.")
|
|
273
|
+
)
|
|
274
|
+
return
|
|
275
|
+
|
|
276
|
+
if not self.agent.tracers:
|
|
277
|
+
logger.warning(
|
|
278
|
+
"No tracer is configured in the agent, no trace id provided."
|
|
279
|
+
)
|
|
280
|
+
return
|
|
281
|
+
|
|
282
|
+
try:
|
|
283
|
+
trace_id = self.agent.tracers[0].trace_id # type: ignore
|
|
284
|
+
logger.info(f"Trace id: {trace_id}")
|
|
285
|
+
except Exception as e:
|
|
286
|
+
logger.warning(f"Get tracer id failed as {e}")
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
def save_tracing_file(self, session_id: str) -> str:
|
|
290
|
+
if not isinstance(
|
|
291
|
+
self.agent, (Agent, SequentialAgent, ParallelAgent, LoopAgent)
|
|
292
|
+
):
|
|
170
293
|
logger.warning(
|
|
171
294
|
(
|
|
172
|
-
"The agent is not an instance of
|
|
295
|
+
"The agent is not an instance of Agent, SequentialAgent, ParallelAgent or LoopAgent, cannot save tracing file."
|
|
173
296
|
)
|
|
174
297
|
)
|
|
175
298
|
return ""
|