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.

Files changed (149) hide show
  1. veadk/agent.py +31 -21
  2. veadk/agents/loop_agent.py +55 -0
  3. veadk/agents/parallel_agent.py +60 -0
  4. veadk/agents/sequential_agent.py +55 -0
  5. veadk/cli/cli_deploy.py +14 -1
  6. veadk/cli/cli_web.py +27 -0
  7. veadk/cloud/cloud_app.py +21 -6
  8. veadk/consts.py +14 -1
  9. veadk/database/viking/viking_database.py +3 -3
  10. veadk/evaluation/adk_evaluator/__init__.py +4 -0
  11. veadk/evaluation/adk_evaluator/adk_evaluator.py +170 -217
  12. veadk/evaluation/base_evaluator.py +26 -20
  13. veadk/evaluation/deepeval_evaluator/deepeval_evaluator.py +8 -5
  14. veadk/{tracing/telemetry/metrics/__init__.py → integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/clean.py} +10 -0
  15. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/app.py +40 -7
  16. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/run.sh +11 -5
  17. veadk/integrations/ve_faas/ve_faas.py +5 -1
  18. veadk/integrations/ve_tos/ve_tos.py +176 -0
  19. veadk/runner.py +162 -39
  20. veadk/tools/builtin_tools/image_edit.py +236 -0
  21. veadk/tools/builtin_tools/image_generate.py +236 -0
  22. veadk/tools/builtin_tools/video_generate.py +326 -0
  23. veadk/tools/sandbox/browser_sandbox.py +19 -9
  24. veadk/tools/sandbox/code_sandbox.py +21 -11
  25. veadk/tools/sandbox/computer_sandbox.py +16 -9
  26. veadk/tracing/base_tracer.py +6 -200
  27. veadk/tracing/telemetry/attributes/attributes.py +29 -0
  28. veadk/tracing/telemetry/attributes/extractors/common_attributes_extractors.py +71 -0
  29. veadk/tracing/telemetry/attributes/extractors/llm_attributes_extractors.py +451 -0
  30. veadk/tracing/telemetry/attributes/extractors/tool_attributes_extractors.py +76 -0
  31. veadk/tracing/telemetry/attributes/extractors/types.py +75 -0
  32. veadk/tracing/telemetry/exporters/apmplus_exporter.py +97 -38
  33. veadk/tracing/telemetry/exporters/base_exporter.py +10 -10
  34. veadk/tracing/telemetry/exporters/cozeloop_exporter.py +20 -13
  35. veadk/tracing/telemetry/exporters/inmemory_exporter.py +49 -32
  36. veadk/tracing/telemetry/exporters/tls_exporter.py +18 -12
  37. veadk/tracing/telemetry/opentelemetry_tracer.py +105 -102
  38. veadk/tracing/telemetry/telemetry.py +238 -0
  39. veadk/types.py +6 -1
  40. veadk/utils/misc.py +41 -1
  41. veadk/utils/patches.py +25 -0
  42. veadk/version.py +1 -1
  43. veadk_python-0.2.5.dist-info/METADATA +345 -0
  44. veadk_python-0.2.5.dist-info/RECORD +127 -0
  45. veadk/__pycache__/__init__.cpython-310.pyc +0 -0
  46. veadk/__pycache__/agent.cpython-310.pyc +0 -0
  47. veadk/__pycache__/config.cpython-310.pyc +0 -0
  48. veadk/__pycache__/consts.cpython-310.pyc +0 -0
  49. veadk/__pycache__/runner.cpython-310.pyc +0 -0
  50. veadk/__pycache__/types.cpython-310.pyc +0 -0
  51. veadk/__pycache__/version.cpython-310.pyc +0 -0
  52. veadk/a2a/__pycache__/__init__.cpython-310.pyc +0 -0
  53. veadk/a2a/__pycache__/agent_card.cpython-310.pyc +0 -0
  54. veadk/a2a/__pycache__/remote_ve_agent.cpython-310.pyc +0 -0
  55. veadk/a2a/__pycache__/ve_a2a_server.cpython-310.pyc +0 -0
  56. veadk/a2a/__pycache__/ve_agent_executor.cpython-310.pyc +0 -0
  57. veadk/cli/__pycache__/__init__.cpython-310.pyc +0 -0
  58. veadk/cli/__pycache__/cli.cpython-310.pyc +0 -0
  59. veadk/cli/__pycache__/cli_deploy.cpython-310.pyc +0 -0
  60. veadk/cli/__pycache__/cli_init.cpython-310.pyc +0 -0
  61. veadk/cli/__pycache__/cli_prompt.cpython-310.pyc +0 -0
  62. veadk/cli/__pycache__/cli_studio.cpython-310.pyc +0 -0
  63. veadk/cli/__pycache__/cli_web.cpython-310.pyc +0 -0
  64. veadk/cli/__pycache__/main.cpython-310.pyc +0 -0
  65. veadk/cloud/__pycache__/__init__.cpython-310.pyc +0 -0
  66. veadk/cloud/__pycache__/cloud_agent_engine.cpython-310.pyc +0 -0
  67. veadk/cloud/__pycache__/cloud_app.cpython-310.pyc +0 -0
  68. veadk/database/__pycache__/__init__.cpython-310.pyc +0 -0
  69. veadk/database/__pycache__/base_database.cpython-310.pyc +0 -0
  70. veadk/database/__pycache__/database_adapter.cpython-310.pyc +0 -0
  71. veadk/database/__pycache__/database_factory.cpython-310.pyc +0 -0
  72. veadk/database/__pycache__/local_database.cpython-310.pyc +0 -0
  73. veadk/database/kv/__pycache__/__init__.cpython-310.pyc +0 -0
  74. veadk/database/relational/__pycache__/__init__.cpython-310.pyc +0 -0
  75. veadk/database/vector/__pycache__/__init__.cpython-310.pyc +0 -0
  76. veadk/database/vector/__pycache__/opensearch_vector_database.cpython-310.pyc +0 -0
  77. veadk/database/vector/__pycache__/type.cpython-310.pyc +0 -0
  78. veadk/database/viking/__pycache__/__init__.cpython-310.pyc +0 -0
  79. veadk/evaluation/__pycache__/__init__.cpython-310.pyc +0 -0
  80. veadk/evaluation/__pycache__/base_evaluator.cpython-310.pyc +0 -0
  81. veadk/evaluation/__pycache__/eval_set_file_loader.cpython-310.pyc +0 -0
  82. veadk/evaluation/__pycache__/eval_set_recorder.cpython-310.pyc +0 -0
  83. veadk/evaluation/__pycache__/types.cpython-310.pyc +0 -0
  84. veadk/evaluation/adk_evaluator/__pycache__/__init__.cpython-310.pyc +0 -0
  85. veadk/evaluation/deepeval_evaluator/__pycache__/__init__.cpython-310.pyc +0 -0
  86. veadk/evaluation/deepeval_evaluator/__pycache__/deepeval_evaluator.cpython-310.pyc +0 -0
  87. veadk/evaluation/utils/__pycache__/prometheus.cpython-310.pyc +0 -0
  88. veadk/integrations/ve_apig/__pycache__/__init__.cpython-310.pyc +0 -0
  89. veadk/integrations/ve_apig/__pycache__/apig.cpython-310.pyc +0 -0
  90. veadk/integrations/ve_apig/__pycache__/ve_apig.cpython-310.pyc +0 -0
  91. veadk/integrations/ve_faas/__pycache__/__init__.cpython-310.pyc +0 -0
  92. veadk/integrations/ve_faas/__pycache__/types.cpython-310.pyc +0 -0
  93. veadk/integrations/ve_faas/__pycache__/ve_faas.cpython-310.pyc +0 -0
  94. veadk/integrations/ve_faas/__pycache__/ve_faas_utils.cpython-310.pyc +0 -0
  95. veadk/integrations/ve_faas/__pycache__/vefaas.cpython-310.pyc +0 -0
  96. veadk/integrations/ve_faas/__pycache__/vefaas_utils.cpython-310.pyc +0 -0
  97. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__pycache__/agent.cpython-310.pyc +0 -0
  98. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__pycache__/app.cpython-310.pyc +0 -0
  99. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__pycache__/studio_app.cpython-310.pyc +0 -0
  100. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name|replace('-', '_') }}/__pycache__/__init__.cpython-310.pyc +0 -0
  101. veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name|replace('-', '_') }}/__pycache__/agent.cpython-310.pyc +0 -0
  102. veadk/integrations/ve_prompt_pilot/__pycache__/__init__.cpython-310.pyc +0 -0
  103. veadk/integrations/ve_prompt_pilot/__pycache__/agentpilot.cpython-310.pyc +0 -0
  104. veadk/knowledgebase/__pycache__/__init__.cpython-310.pyc +0 -0
  105. veadk/knowledgebase/__pycache__/knowledgebase.cpython-310.pyc +0 -0
  106. veadk/knowledgebase/__pycache__/knowledgebase_database_adapter.cpython-310.pyc +0 -0
  107. veadk/memory/__pycache__/__init__.cpython-310.pyc +0 -0
  108. veadk/memory/__pycache__/long_term_memory.cpython-310.pyc +0 -0
  109. veadk/memory/__pycache__/memory_database_adapter.cpython-310.pyc +0 -0
  110. veadk/memory/__pycache__/short_term_memory.cpython-310.pyc +0 -0
  111. veadk/memory/__pycache__/short_term_memory_processor.cpython-310.pyc +0 -0
  112. veadk/prompts/__pycache__/__init__.cpython-310.pyc +0 -0
  113. veadk/prompts/__pycache__/agent_default_prompt.cpython-310.pyc +0 -0
  114. veadk/prompts/__pycache__/prompt_memory_processor.cpython-310.pyc +0 -0
  115. veadk/prompts/__pycache__/prompt_optimization.cpython-310.pyc +0 -0
  116. veadk/tools/__pycache__/__init__.cpython-310.pyc +0 -0
  117. veadk/tools/__pycache__/demo_tools.cpython-310.pyc +0 -0
  118. veadk/tools/__pycache__/load_knowledgebase_tool.cpython-310.pyc +0 -0
  119. veadk/tools/builtin_tools/__pycache__/__init__.cpython-310.pyc +0 -0
  120. veadk/tools/builtin_tools/__pycache__/lark.cpython-310.pyc +0 -0
  121. veadk/tools/builtin_tools/__pycache__/vesearch.cpython-310.pyc +0 -0
  122. veadk/tools/builtin_tools/__pycache__/web_search.cpython-310.pyc +0 -0
  123. veadk/tools/sandbox/__pycache__/__init__.cpython-310.pyc +0 -0
  124. veadk/tracing/__pycache__/__init__.cpython-310.pyc +0 -0
  125. veadk/tracing/__pycache__/base_tracer.cpython-310.pyc +0 -0
  126. veadk/tracing/telemetry/__pycache__/__init__.cpython-310.pyc +0 -0
  127. veadk/tracing/telemetry/__pycache__/opentelemetry_tracer.cpython-310.pyc +0 -0
  128. veadk/tracing/telemetry/exporters/__pycache__/__init__.cpython-310.pyc +0 -0
  129. veadk/tracing/telemetry/exporters/__pycache__/apiserver_exporter.cpython-310.pyc +0 -0
  130. veadk/tracing/telemetry/exporters/__pycache__/apmplus_exporter.cpython-310.pyc +0 -0
  131. veadk/tracing/telemetry/exporters/__pycache__/base_exporter.cpython-310.pyc +0 -0
  132. veadk/tracing/telemetry/exporters/__pycache__/cozeloop_exporter.cpython-310.pyc +0 -0
  133. veadk/tracing/telemetry/exporters/__pycache__/inmemory_exporter.cpython-310.pyc +0 -0
  134. veadk/tracing/telemetry/exporters/__pycache__/tls_exporter.cpython-310.pyc +0 -0
  135. veadk/tracing/telemetry/metrics/__pycache__/__init__.cpython-310.pyc +0 -0
  136. veadk/tracing/telemetry/metrics/__pycache__/opentelemetry_metrics.cpython-310.pyc +0 -0
  137. veadk/tracing/telemetry/metrics/opentelemetry_metrics.py +0 -73
  138. veadk/utils/__pycache__/__init__.cpython-310.pyc +0 -0
  139. veadk/utils/__pycache__/logger.cpython-310.pyc +0 -0
  140. veadk/utils/__pycache__/mcp_utils.cpython-310.pyc +0 -0
  141. veadk/utils/__pycache__/misc.cpython-310.pyc +0 -0
  142. veadk/utils/__pycache__/patches.cpython-310.pyc +0 -0
  143. veadk/utils/__pycache__/volcengine_sign.cpython-310.pyc +0 -0
  144. veadk_python-0.2.2.dist-info/METADATA +0 -144
  145. veadk_python-0.2.2.dist-info/RECORD +0 -213
  146. {veadk_python-0.2.2.dist-info → veadk_python-0.2.5.dist-info}/WHEEL +0 -0
  147. {veadk_python-0.2.2.dist-info → veadk_python-0.2.5.dist-info}/entry_points.txt +0 -0
  148. {veadk_python-0.2.2.dist-info → veadk_python-0.2.5.dist-info}/licenses/LICENSE +0 -0
  149. {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(title=a2a_app.title, version=a2a_app.version, lifespan=combined_lifespan)
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 uvicorn and fastapi not installed in user's requirements.txt
37
- python3 -m pip install uvicorn[standard]
38
-
39
- python3 -m pip install fastapi
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
- python3 -m pip install fastmcp
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
- logger.debug(f"Function creation response: {res}")
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: Agent | RemoteVeAgent,
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
- self.short_term_memory = short_term_memory
58
- self.session_service = short_term_memory.session_service
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=messages.media,
90
- data=read_png_to_bytes(messages.media),
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
- async def event_generator():
116
- async for event in self.runner.run_async(
117
- user_id=self.user_id,
118
- session_id=session_id,
119
- new_message=message,
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
- final_output = ""
134
- async for chunk in event_generator():
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(chunk, end="", flush=True)
137
- final_output += chunk
138
- if stream:
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(session_id, converted_message, stream)
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 save_tracing_file(self, session_id: str) -> str:
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 VeADK Agent, cannot save tracing file."
295
+ "The agent is not an instance of Agent, SequentialAgent, ParallelAgent or LoopAgent, cannot save tracing file."
173
296
  )
174
297
  )
175
298
  return ""