veadk-python 0.2.27__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- veadk/__init__.py +37 -0
- veadk/a2a/__init__.py +13 -0
- veadk/a2a/agent_card.py +45 -0
- veadk/a2a/remote_ve_agent.py +390 -0
- veadk/a2a/utils/__init__.py +13 -0
- veadk/a2a/utils/agent_to_a2a.py +170 -0
- veadk/a2a/ve_a2a_server.py +93 -0
- veadk/a2a/ve_agent_executor.py +78 -0
- veadk/a2a/ve_middlewares.py +313 -0
- veadk/a2a/ve_task_store.py +37 -0
- veadk/agent.py +402 -0
- veadk/agent_builder.py +93 -0
- veadk/agents/loop_agent.py +68 -0
- veadk/agents/parallel_agent.py +72 -0
- veadk/agents/sequential_agent.py +64 -0
- veadk/auth/__init__.py +13 -0
- veadk/auth/base_auth.py +22 -0
- veadk/auth/ve_credential_service.py +203 -0
- veadk/auth/veauth/__init__.py +13 -0
- veadk/auth/veauth/apmplus_veauth.py +58 -0
- veadk/auth/veauth/ark_veauth.py +75 -0
- veadk/auth/veauth/base_veauth.py +50 -0
- veadk/auth/veauth/cozeloop_veauth.py +13 -0
- veadk/auth/veauth/opensearch_veauth.py +75 -0
- veadk/auth/veauth/postgresql_veauth.py +75 -0
- veadk/auth/veauth/prompt_pilot_veauth.py +60 -0
- veadk/auth/veauth/speech_veauth.py +54 -0
- veadk/auth/veauth/utils.py +69 -0
- veadk/auth/veauth/vesearch_veauth.py +62 -0
- veadk/auth/veauth/viking_mem0_veauth.py +91 -0
- veadk/cli/__init__.py +13 -0
- veadk/cli/cli.py +58 -0
- veadk/cli/cli_clean.py +87 -0
- veadk/cli/cli_create.py +163 -0
- veadk/cli/cli_deploy.py +233 -0
- veadk/cli/cli_eval.py +215 -0
- veadk/cli/cli_init.py +214 -0
- veadk/cli/cli_kb.py +110 -0
- veadk/cli/cli_pipeline.py +285 -0
- veadk/cli/cli_prompt.py +86 -0
- veadk/cli/cli_update.py +106 -0
- veadk/cli/cli_uploadevalset.py +139 -0
- veadk/cli/cli_web.py +143 -0
- veadk/cloud/__init__.py +13 -0
- veadk/cloud/cloud_agent_engine.py +485 -0
- veadk/cloud/cloud_app.py +475 -0
- veadk/config.py +115 -0
- veadk/configs/__init__.py +13 -0
- veadk/configs/auth_configs.py +133 -0
- veadk/configs/database_configs.py +132 -0
- veadk/configs/model_configs.py +78 -0
- veadk/configs/tool_configs.py +54 -0
- veadk/configs/tracing_configs.py +110 -0
- veadk/consts.py +74 -0
- veadk/evaluation/__init__.py +17 -0
- veadk/evaluation/adk_evaluator/__init__.py +17 -0
- veadk/evaluation/adk_evaluator/adk_evaluator.py +302 -0
- veadk/evaluation/base_evaluator.py +642 -0
- veadk/evaluation/deepeval_evaluator/__init__.py +17 -0
- veadk/evaluation/deepeval_evaluator/deepeval_evaluator.py +339 -0
- veadk/evaluation/eval_set_file_loader.py +48 -0
- veadk/evaluation/eval_set_recorder.py +146 -0
- veadk/evaluation/types.py +65 -0
- veadk/evaluation/utils/prometheus.py +196 -0
- veadk/integrations/__init__.py +13 -0
- veadk/integrations/ve_apig/__init__.py +13 -0
- veadk/integrations/ve_apig/ve_apig.py +349 -0
- veadk/integrations/ve_apig/ve_apig_utils.py +332 -0
- veadk/integrations/ve_code_pipeline/__init__.py +13 -0
- veadk/integrations/ve_code_pipeline/ve_code_pipeline.py +431 -0
- veadk/integrations/ve_cozeloop/__init__.py +13 -0
- veadk/integrations/ve_cozeloop/ve_cozeloop.py +96 -0
- veadk/integrations/ve_cr/__init__.py +13 -0
- veadk/integrations/ve_cr/ve_cr.py +220 -0
- veadk/integrations/ve_faas/__init__.py +13 -0
- veadk/integrations/ve_faas/template/cookiecutter.json +15 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/__init__.py +13 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/config.yaml.example +6 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/deploy.py +106 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/__init__.py +13 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/agent.py +25 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/app.py +202 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/requirements.txt +3 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/run.sh +49 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name }}/__init__.py +14 -0
- veadk/integrations/ve_faas/template/{{cookiecutter.local_dir_name}}/src/{{ cookiecutter.app_name }}/agent.py +27 -0
- veadk/integrations/ve_faas/ve_faas.py +754 -0
- veadk/integrations/ve_faas/ve_faas_utils.py +408 -0
- veadk/integrations/ve_faas/web_template/cookiecutter.json +20 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/__init__.py +13 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/clean.py +23 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/config.yaml.example +2 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/deploy.py +44 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/Dockerfile +23 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/app.py +123 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/init_db.py +46 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/models.py +36 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/requirements.txt +4 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/run.sh +21 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/css/style.css +368 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/static/js/admin.js +0 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/dashboard.html +21 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/edit_post.html +24 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/login.html +21 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/admin/posts.html +53 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/base.html +45 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/index.html +29 -0
- veadk/integrations/ve_faas/web_template/{{cookiecutter.local_dir_name}}/src/templates/post.html +14 -0
- veadk/integrations/ve_identity/__init__.py +110 -0
- veadk/integrations/ve_identity/auth_config.py +261 -0
- veadk/integrations/ve_identity/auth_mixins.py +650 -0
- veadk/integrations/ve_identity/auth_processor.py +385 -0
- veadk/integrations/ve_identity/function_tool.py +158 -0
- veadk/integrations/ve_identity/identity_client.py +864 -0
- veadk/integrations/ve_identity/mcp_tool.py +181 -0
- veadk/integrations/ve_identity/mcp_toolset.py +431 -0
- veadk/integrations/ve_identity/models.py +228 -0
- veadk/integrations/ve_identity/token_manager.py +188 -0
- veadk/integrations/ve_identity/utils.py +151 -0
- veadk/integrations/ve_prompt_pilot/__init__.py +13 -0
- veadk/integrations/ve_prompt_pilot/ve_prompt_pilot.py +85 -0
- veadk/integrations/ve_tls/__init__.py +13 -0
- veadk/integrations/ve_tls/utils.py +116 -0
- veadk/integrations/ve_tls/ve_tls.py +212 -0
- veadk/integrations/ve_tos/ve_tos.py +710 -0
- veadk/integrations/ve_viking_db_memory/__init__.py +13 -0
- veadk/integrations/ve_viking_db_memory/ve_viking_db_memory.py +308 -0
- veadk/knowledgebase/__init__.py +17 -0
- veadk/knowledgebase/backends/__init__.py +13 -0
- veadk/knowledgebase/backends/base_backend.py +72 -0
- veadk/knowledgebase/backends/in_memory_backend.py +91 -0
- veadk/knowledgebase/backends/opensearch_backend.py +162 -0
- veadk/knowledgebase/backends/redis_backend.py +172 -0
- veadk/knowledgebase/backends/utils.py +92 -0
- veadk/knowledgebase/backends/vikingdb_knowledge_backend.py +608 -0
- veadk/knowledgebase/entry.py +25 -0
- veadk/knowledgebase/knowledgebase.py +307 -0
- veadk/memory/__init__.py +35 -0
- veadk/memory/long_term_memory.py +365 -0
- veadk/memory/long_term_memory_backends/__init__.py +13 -0
- veadk/memory/long_term_memory_backends/base_backend.py +35 -0
- veadk/memory/long_term_memory_backends/in_memory_backend.py +67 -0
- veadk/memory/long_term_memory_backends/mem0_backend.py +155 -0
- veadk/memory/long_term_memory_backends/opensearch_backend.py +124 -0
- veadk/memory/long_term_memory_backends/redis_backend.py +140 -0
- veadk/memory/long_term_memory_backends/vikingdb_memory_backend.py +189 -0
- veadk/memory/short_term_memory.py +252 -0
- veadk/memory/short_term_memory_backends/__init__.py +13 -0
- veadk/memory/short_term_memory_backends/base_backend.py +31 -0
- veadk/memory/short_term_memory_backends/mysql_backend.py +49 -0
- veadk/memory/short_term_memory_backends/postgresql_backend.py +49 -0
- veadk/memory/short_term_memory_backends/sqlite_backend.py +55 -0
- veadk/memory/short_term_memory_processor.py +100 -0
- veadk/processors/__init__.py +26 -0
- veadk/processors/base_run_processor.py +120 -0
- veadk/prompts/__init__.py +13 -0
- veadk/prompts/agent_default_prompt.py +30 -0
- veadk/prompts/prompt_evaluator.py +20 -0
- veadk/prompts/prompt_memory_processor.py +55 -0
- veadk/prompts/prompt_optimization.py +150 -0
- veadk/runner.py +732 -0
- veadk/tools/__init__.py +13 -0
- veadk/tools/builtin_tools/__init__.py +13 -0
- veadk/tools/builtin_tools/agent_authorization.py +94 -0
- veadk/tools/builtin_tools/generate_image.py +23 -0
- veadk/tools/builtin_tools/image_edit.py +300 -0
- veadk/tools/builtin_tools/image_generate.py +446 -0
- veadk/tools/builtin_tools/lark.py +67 -0
- veadk/tools/builtin_tools/las.py +24 -0
- veadk/tools/builtin_tools/link_reader.py +66 -0
- veadk/tools/builtin_tools/llm_shield.py +381 -0
- veadk/tools/builtin_tools/load_knowledgebase.py +97 -0
- veadk/tools/builtin_tools/mcp_router.py +29 -0
- veadk/tools/builtin_tools/run_code.py +113 -0
- veadk/tools/builtin_tools/tts.py +253 -0
- veadk/tools/builtin_tools/vesearch.py +49 -0
- veadk/tools/builtin_tools/video_generate.py +363 -0
- veadk/tools/builtin_tools/web_scraper.py +76 -0
- veadk/tools/builtin_tools/web_search.py +83 -0
- veadk/tools/demo_tools.py +58 -0
- veadk/tools/load_knowledgebase_tool.py +149 -0
- veadk/tools/sandbox/__init__.py +13 -0
- veadk/tools/sandbox/browser_sandbox.py +37 -0
- veadk/tools/sandbox/code_sandbox.py +40 -0
- veadk/tools/sandbox/computer_sandbox.py +34 -0
- veadk/tracing/__init__.py +13 -0
- veadk/tracing/base_tracer.py +58 -0
- veadk/tracing/telemetry/__init__.py +13 -0
- veadk/tracing/telemetry/attributes/attributes.py +29 -0
- veadk/tracing/telemetry/attributes/extractors/common_attributes_extractors.py +180 -0
- veadk/tracing/telemetry/attributes/extractors/llm_attributes_extractors.py +858 -0
- veadk/tracing/telemetry/attributes/extractors/tool_attributes_extractors.py +152 -0
- veadk/tracing/telemetry/attributes/extractors/types.py +164 -0
- veadk/tracing/telemetry/exporters/__init__.py +13 -0
- veadk/tracing/telemetry/exporters/apmplus_exporter.py +558 -0
- veadk/tracing/telemetry/exporters/base_exporter.py +39 -0
- veadk/tracing/telemetry/exporters/cozeloop_exporter.py +129 -0
- veadk/tracing/telemetry/exporters/inmemory_exporter.py +248 -0
- veadk/tracing/telemetry/exporters/tls_exporter.py +139 -0
- veadk/tracing/telemetry/opentelemetry_tracer.py +320 -0
- veadk/tracing/telemetry/telemetry.py +411 -0
- veadk/types.py +47 -0
- veadk/utils/__init__.py +13 -0
- veadk/utils/audio_manager.py +95 -0
- veadk/utils/auth.py +294 -0
- veadk/utils/logger.py +59 -0
- veadk/utils/mcp_utils.py +44 -0
- veadk/utils/misc.py +184 -0
- veadk/utils/patches.py +101 -0
- veadk/utils/volcengine_sign.py +205 -0
- veadk/version.py +15 -0
- veadk_python-0.2.27.dist-info/METADATA +373 -0
- veadk_python-0.2.27.dist-info/RECORD +218 -0
- veadk_python-0.2.27.dist-info/WHEEL +5 -0
- veadk_python-0.2.27.dist-info/entry_points.txt +2 -0
- veadk_python-0.2.27.dist-info/licenses/LICENSE +201 -0
- veadk_python-0.2.27.dist-info/top_level.txt +1 -0
veadk/runner.py
ADDED
|
@@ -0,0 +1,732 @@
|
|
|
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 functools
|
|
16
|
+
import os
|
|
17
|
+
from types import MethodType
|
|
18
|
+
from typing import Union
|
|
19
|
+
|
|
20
|
+
from google import genai
|
|
21
|
+
from google.adk.agents import RunConfig
|
|
22
|
+
from google.adk.agents.base_agent import BaseAgent
|
|
23
|
+
from google.adk.agents.invocation_context import LlmCallsLimitExceededError
|
|
24
|
+
from google.adk.runners import Runner as ADKRunner
|
|
25
|
+
from google.genai import types
|
|
26
|
+
from google.genai.types import Blob
|
|
27
|
+
|
|
28
|
+
from veadk.agent import Agent
|
|
29
|
+
from veadk.agents.loop_agent import LoopAgent
|
|
30
|
+
from veadk.agents.parallel_agent import ParallelAgent
|
|
31
|
+
from veadk.agents.sequential_agent import SequentialAgent
|
|
32
|
+
from veadk.config import getenv
|
|
33
|
+
from veadk.evaluation import EvalSetRecorder
|
|
34
|
+
from veadk.memory.short_term_memory import ShortTermMemory
|
|
35
|
+
from veadk.processors.base_run_processor import BaseRunProcessor
|
|
36
|
+
from veadk.types import MediaMessage
|
|
37
|
+
from veadk.utils.logger import get_logger
|
|
38
|
+
from veadk.utils.misc import formatted_timestamp, read_file_to_bytes
|
|
39
|
+
|
|
40
|
+
logger = get_logger(__name__)
|
|
41
|
+
|
|
42
|
+
RunnerMessage = Union[
|
|
43
|
+
str, # single turn text-based prompt
|
|
44
|
+
list[str], # multiple turn text-based prompt
|
|
45
|
+
MediaMessage, # single turn prompt with media
|
|
46
|
+
list[MediaMessage], # multiple turn prompt with media
|
|
47
|
+
list[MediaMessage | str], # multiple turn prompt with media and text-based prompt
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
async def pre_run_process(self, process_func, new_message, user_id, session_id):
|
|
52
|
+
"""Pre-run hook invoked before agent execution.
|
|
53
|
+
|
|
54
|
+
Iterates over all ``parts`` of ``new_message`` and, when a ``part`` contains
|
|
55
|
+
``inline_data`` and uploading is enabled, calls ``process_func`` to process
|
|
56
|
+
the data (for example, upload to TOS and rewrite with an accessible URL).
|
|
57
|
+
Typically used together with the ``intercept_new_message`` decorator.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
self: Runner instance.
|
|
61
|
+
process_func: An async processing function with a signature like
|
|
62
|
+
``(part, app_name, user_id, session_id)`` used to handle
|
|
63
|
+
``inline_data`` in the message (e.g., upload to TOS).
|
|
64
|
+
new_message (google.genai.types.Content): Incoming user message.
|
|
65
|
+
user_id (str): User identifier.
|
|
66
|
+
session_id (str): Session identifier.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
None
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
Exception: Propagated if ``process_func`` raises and does not handle it.
|
|
73
|
+
"""
|
|
74
|
+
if new_message.parts:
|
|
75
|
+
for part in new_message.parts:
|
|
76
|
+
if part.inline_data and self.upload_inline_data_to_tos:
|
|
77
|
+
await process_func(
|
|
78
|
+
part,
|
|
79
|
+
self.app_name,
|
|
80
|
+
user_id,
|
|
81
|
+
session_id,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def post_run_process(self):
|
|
86
|
+
"""Post-run hook executed after agent run.
|
|
87
|
+
|
|
88
|
+
This is currently a no-op placeholder and can be extended to perform
|
|
89
|
+
cleanup or finalize logic after a run.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
self: Runner instance.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
None
|
|
96
|
+
|
|
97
|
+
Raises:
|
|
98
|
+
None
|
|
99
|
+
"""
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def intercept_new_message(process_func):
|
|
104
|
+
"""Create a decorator to insert pre/post hooks around ``run_async`` calls.
|
|
105
|
+
|
|
106
|
+
Internally it invokes :func:`pre_run_process` to preprocess the incoming
|
|
107
|
+
message (e.g., upload image/video inline data to TOS), then iterates the
|
|
108
|
+
underlying event stream and finally calls :func:`post_run_process`.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
process_func: Async function used to process ``inline_data`` (typically
|
|
112
|
+
``_upload_image_to_tos``).
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Callable: A decorator that can wrap ``run_async``.
|
|
116
|
+
|
|
117
|
+
Raises:
|
|
118
|
+
Exception: May propagate exceptions raised by the wrapped function or
|
|
119
|
+
the pre-processing step.
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
def decorator(func):
|
|
123
|
+
@functools.wraps(func)
|
|
124
|
+
async def wrapper(
|
|
125
|
+
self,
|
|
126
|
+
*,
|
|
127
|
+
user_id: str,
|
|
128
|
+
session_id: str,
|
|
129
|
+
new_message: types.Content,
|
|
130
|
+
**kwargs,
|
|
131
|
+
):
|
|
132
|
+
await pre_run_process(self, process_func, new_message, user_id, session_id)
|
|
133
|
+
|
|
134
|
+
async for event in func(
|
|
135
|
+
user_id=user_id,
|
|
136
|
+
session_id=session_id,
|
|
137
|
+
new_message=new_message,
|
|
138
|
+
**kwargs,
|
|
139
|
+
):
|
|
140
|
+
yield event
|
|
141
|
+
if event.get_function_calls():
|
|
142
|
+
for function_call in event.get_function_calls():
|
|
143
|
+
logger.debug(f"Function call: {function_call}")
|
|
144
|
+
elif (
|
|
145
|
+
event.content is not None
|
|
146
|
+
and event.content.parts
|
|
147
|
+
and event.content.parts[0].text is not None
|
|
148
|
+
and len(event.content.parts[0].text.strip()) > 0
|
|
149
|
+
):
|
|
150
|
+
final_output = event.content.parts[0].text
|
|
151
|
+
logger.debug(f"Event output: {final_output}")
|
|
152
|
+
|
|
153
|
+
post_run_process(self)
|
|
154
|
+
|
|
155
|
+
return wrapper
|
|
156
|
+
|
|
157
|
+
return decorator
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _convert_messages(
|
|
161
|
+
messages: RunnerMessage,
|
|
162
|
+
app_name: str,
|
|
163
|
+
user_id: str,
|
|
164
|
+
session_id: str,
|
|
165
|
+
) -> list:
|
|
166
|
+
"""Convert a VeADK ``RunnerMessage`` into a list of Google ADK messages.
|
|
167
|
+
|
|
168
|
+
Supported inputs:
|
|
169
|
+
- ``str``: Single-turn text prompt.
|
|
170
|
+
- :class:`veadk.types.MediaMessage`: Single-turn multimodal prompt (text + image/video).
|
|
171
|
+
- ``list``: A list of the above types (multi-turn with mixed text and multimodal).
|
|
172
|
+
|
|
173
|
+
For multimodal inputs, this reads the local media file bytes and detects
|
|
174
|
+
the MIME type via ``filetype``; only ``image/*`` and ``video/*`` are supported.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
messages (RunnerMessage): Input message or list of messages to convert.
|
|
178
|
+
app_name (str): App name (not directly used; kept for consistency with upload path).
|
|
179
|
+
user_id (str): User ID (not directly used; kept for consistency with upload path).
|
|
180
|
+
session_id (str): Session ID (not directly used; kept for consistency with upload path).
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
list[google.genai.types.Content]: Converted ADK messages.
|
|
184
|
+
|
|
185
|
+
Raises:
|
|
186
|
+
ValueError: If the message type is unknown or media type is unrecognized.
|
|
187
|
+
AssertionError: If the media MIME type is not supported (only image/* and video/*).
|
|
188
|
+
|
|
189
|
+
Note:
|
|
190
|
+
This function only performs structural conversion. To upload inline media
|
|
191
|
+
to an object store and rewrite URLs, use it together with
|
|
192
|
+
``intercept_new_message`` and ``_upload_image_to_tos``.
|
|
193
|
+
"""
|
|
194
|
+
if isinstance(messages, str):
|
|
195
|
+
_messages = [types.Content(role="user", parts=[types.Part(text=messages)])]
|
|
196
|
+
elif isinstance(messages, MediaMessage):
|
|
197
|
+
import filetype
|
|
198
|
+
|
|
199
|
+
file_data = read_file_to_bytes(messages.media)
|
|
200
|
+
|
|
201
|
+
kind = filetype.guess(file_data)
|
|
202
|
+
if kind is None:
|
|
203
|
+
raise ValueError("Unsupported or unknown file type.")
|
|
204
|
+
|
|
205
|
+
mime_type = kind.mime
|
|
206
|
+
|
|
207
|
+
assert mime_type.startswith(("image/", "video/")), (
|
|
208
|
+
f"Unsupported media type: {mime_type}"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
_messages = [
|
|
212
|
+
types.Content(
|
|
213
|
+
role="user",
|
|
214
|
+
parts=[
|
|
215
|
+
types.Part(text=messages.text),
|
|
216
|
+
types.Part(
|
|
217
|
+
inline_data=Blob(
|
|
218
|
+
display_name=messages.media,
|
|
219
|
+
data=file_data,
|
|
220
|
+
mime_type=mime_type,
|
|
221
|
+
)
|
|
222
|
+
),
|
|
223
|
+
],
|
|
224
|
+
)
|
|
225
|
+
]
|
|
226
|
+
elif isinstance(messages, list):
|
|
227
|
+
converted_messages = []
|
|
228
|
+
for message in messages:
|
|
229
|
+
converted_messages.extend(
|
|
230
|
+
_convert_messages(message, app_name, user_id, session_id)
|
|
231
|
+
)
|
|
232
|
+
_messages = converted_messages
|
|
233
|
+
else:
|
|
234
|
+
raise ValueError(f"Unknown message type: {type(messages)}")
|
|
235
|
+
|
|
236
|
+
return _messages
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
async def _upload_image_to_tos(
|
|
240
|
+
part: genai.types.Part, app_name: str, user_id: str, session_id: str
|
|
241
|
+
) -> None:
|
|
242
|
+
"""Upload inline media data in a message part to TOS and rewrite its URL.
|
|
243
|
+
|
|
244
|
+
When ``part.inline_data`` has both ``display_name`` (original filename) and
|
|
245
|
+
``data`` (bytes), it generates an object storage path based on
|
|
246
|
+
``app_name``, ``user_id`` and ``session_id``. After upload, it replaces
|
|
247
|
+
``display_name`` with a signed URL.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
part (google.genai.types.Part): Message part containing ``inline_data``.
|
|
251
|
+
app_name (str): App name.
|
|
252
|
+
user_id (str): User ID.
|
|
253
|
+
session_id (str): Session ID.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
None
|
|
257
|
+
|
|
258
|
+
Raises:
|
|
259
|
+
None: All exceptions are caught and logged; nothing is propagated.
|
|
260
|
+
"""
|
|
261
|
+
try:
|
|
262
|
+
if part.inline_data and part.inline_data.display_name and part.inline_data.data:
|
|
263
|
+
from veadk.integrations.ve_tos.ve_tos import VeTOS
|
|
264
|
+
|
|
265
|
+
filename = os.path.basename(part.inline_data.display_name)
|
|
266
|
+
object_key = f"{app_name}/{user_id}-{session_id}-{filename}"
|
|
267
|
+
ve_tos = VeTOS()
|
|
268
|
+
tos_url = ve_tos.build_tos_signed_url(object_key=object_key)
|
|
269
|
+
await ve_tos.async_upload_bytes(
|
|
270
|
+
object_key=object_key,
|
|
271
|
+
data=part.inline_data.data,
|
|
272
|
+
)
|
|
273
|
+
part.inline_data.display_name = tos_url
|
|
274
|
+
except Exception as e:
|
|
275
|
+
logger.error(f"Upload to TOS failed: {e}")
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
class Runner(ADKRunner):
|
|
279
|
+
"""VeADK Runner that augments ADK with session, memory, tracing, and media upload.
|
|
280
|
+
|
|
281
|
+
This class builds on Google ADK's ``Runner`` and adds:
|
|
282
|
+
- Integration with short-term memory (ShortTermMemory) for auto session management.
|
|
283
|
+
- Optional long-term memory integration and session persistence.
|
|
284
|
+
- New message interception and media upload to TOS.
|
|
285
|
+
- Tracing dump and Trace ID logging.
|
|
286
|
+
- A simplified ``run`` entry that supports multi-turn text/multimodal inputs.
|
|
287
|
+
|
|
288
|
+
Attributes:
|
|
289
|
+
user_id (str): Default user ID.
|
|
290
|
+
long_term_memory: Long-term memory service instance, or ``None`` if not set.
|
|
291
|
+
short_term_memory (veadk.memory.short_term_memory.ShortTermMemory | None):
|
|
292
|
+
Short-term memory instance used to auto-create/manage sessions.
|
|
293
|
+
upload_inline_data_to_tos (bool): Whether to upload inline media to TOS while running.
|
|
294
|
+
session_service: Session service instance (may come from short-term memory).
|
|
295
|
+
memory_service: Memory service instance (may come from agent's long-term memory).
|
|
296
|
+
app_name (str): Application name used in session management and object pathing.
|
|
297
|
+
|
|
298
|
+
Note:
|
|
299
|
+
This class wraps the parent ``run_async`` at initialization to insert media
|
|
300
|
+
upload and post-run handling. If you override the underlying ``run_async``,
|
|
301
|
+
ensure it remains compatible with this interception logic.
|
|
302
|
+
"""
|
|
303
|
+
|
|
304
|
+
def __init__(
|
|
305
|
+
self,
|
|
306
|
+
agent: BaseAgent | Agent | None = None,
|
|
307
|
+
short_term_memory: ShortTermMemory | None = None,
|
|
308
|
+
app_name: str | None = None,
|
|
309
|
+
user_id: str = "veadk_default_user",
|
|
310
|
+
upload_inline_data_to_tos: bool = False,
|
|
311
|
+
run_processor: "BaseRunProcessor | None" = None,
|
|
312
|
+
*args,
|
|
313
|
+
**kwargs,
|
|
314
|
+
) -> None:
|
|
315
|
+
"""Initialize a Runner instance.
|
|
316
|
+
|
|
317
|
+
Selects the session service based on provided short-term memory or an
|
|
318
|
+
external ``session_service``. If long-term memory or an external
|
|
319
|
+
``memory_service`` is provided, the passed service is preferred. After
|
|
320
|
+
construction, it injects a message interception layer into the parent's
|
|
321
|
+
``run_async`` to support inline media upload and post-run handling.
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
agent (google.adk.agents.base_agent.BaseAgent | veadk.agent.Agent):
|
|
325
|
+
The agent instance used to run interactions.
|
|
326
|
+
short_term_memory (ShortTermMemory | None): Optional short-term memory; if
|
|
327
|
+
not provided and no external `session_service` is supplied, an in-memory
|
|
328
|
+
session service will be created.
|
|
329
|
+
app_name (str): Application name. Defaults to `veadk_default_app`.
|
|
330
|
+
user_id (str): Default user ID. Defaults to `veadk_default_user`.
|
|
331
|
+
upload_inline_data_to_tos (bool): Whether to enable inline media upload. Defaults to `False`.
|
|
332
|
+
run_processor (BaseRunProcessor | None): Optional run processor for intercepting agent execution.
|
|
333
|
+
If not provided, will try to get from agent. If agent doesn't have one, uses NoOpRunProcessor.
|
|
334
|
+
*args: Positional args passed through to `ADKRunner`.
|
|
335
|
+
**kwargs: Keyword args passed through to `ADKRunner`; may include
|
|
336
|
+
``session_service`` and ``memory_service`` to override defaults.
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
None
|
|
340
|
+
|
|
341
|
+
Raises:
|
|
342
|
+
None
|
|
343
|
+
"""
|
|
344
|
+
self.user_id = user_id
|
|
345
|
+
self.long_term_memory = None
|
|
346
|
+
self.short_term_memory = short_term_memory
|
|
347
|
+
self.upload_inline_data_to_tos = upload_inline_data_to_tos
|
|
348
|
+
credential_service = kwargs.pop("credential_service", None)
|
|
349
|
+
session_service = kwargs.pop("session_service", None)
|
|
350
|
+
memory_service = kwargs.pop("memory_service", None)
|
|
351
|
+
|
|
352
|
+
# Handle run_processor: priority is runner arg > agent.run_processor > NoOpRunProcessor
|
|
353
|
+
if run_processor is not None:
|
|
354
|
+
self.run_processor = run_processor
|
|
355
|
+
elif hasattr(agent, "run_processor") and agent.run_processor is not None: # type: ignore
|
|
356
|
+
self.run_processor = agent.run_processor # type: ignore
|
|
357
|
+
else:
|
|
358
|
+
from veadk.processors import NoOpRunProcessor
|
|
359
|
+
|
|
360
|
+
self.run_processor = NoOpRunProcessor()
|
|
361
|
+
|
|
362
|
+
if session_service:
|
|
363
|
+
if short_term_memory:
|
|
364
|
+
logger.warning(
|
|
365
|
+
"Short term memory is enabled, but session service is also provided. We will use session service from runner argument."
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
if not session_service:
|
|
369
|
+
if short_term_memory:
|
|
370
|
+
session_service = short_term_memory.session_service
|
|
371
|
+
logger.debug(
|
|
372
|
+
f"Use session service {session_service} from short term memory."
|
|
373
|
+
)
|
|
374
|
+
else:
|
|
375
|
+
logger.warning(
|
|
376
|
+
"No short term memory or session service provided, use an in-memory one instead."
|
|
377
|
+
)
|
|
378
|
+
short_term_memory = ShortTermMemory()
|
|
379
|
+
self.short_term_memory = short_term_memory
|
|
380
|
+
session_service = short_term_memory.session_service
|
|
381
|
+
|
|
382
|
+
if memory_service:
|
|
383
|
+
if hasattr(agent, "long_term_memory") and agent.long_term_memory: # type: ignore
|
|
384
|
+
self.long_term_memory = agent.long_term_memory # type: ignore
|
|
385
|
+
logger.warning(
|
|
386
|
+
"Long term memory in agent is enabled, but memory service is also provided. We will use memory service from runner argument."
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
if not memory_service:
|
|
390
|
+
if hasattr(agent, "long_term_memory") and agent.long_term_memory: # type: ignore
|
|
391
|
+
self.long_term_memory = agent.long_term_memory # type: ignore
|
|
392
|
+
memory_service = agent.long_term_memory # type: ignore
|
|
393
|
+
else:
|
|
394
|
+
logger.info("No long term memory provided.")
|
|
395
|
+
|
|
396
|
+
# For forward compatibility, we pass app_name to ADKRunner.
|
|
397
|
+
if not kwargs.get("app") and not app_name:
|
|
398
|
+
app_name = "veadk_default_app"
|
|
399
|
+
|
|
400
|
+
super().__init__(
|
|
401
|
+
agent=agent,
|
|
402
|
+
session_service=session_service,
|
|
403
|
+
memory_service=memory_service,
|
|
404
|
+
credential_service=credential_service,
|
|
405
|
+
app_name=app_name,
|
|
406
|
+
*args,
|
|
407
|
+
**kwargs,
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
self.run_async = MethodType(
|
|
411
|
+
intercept_new_message(_upload_image_to_tos)(super().run_async), self
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
async def run(
|
|
415
|
+
self,
|
|
416
|
+
messages: RunnerMessage,
|
|
417
|
+
user_id: str = "",
|
|
418
|
+
session_id: str = f"tmp-session-{formatted_timestamp()}",
|
|
419
|
+
run_config: RunConfig | None = None,
|
|
420
|
+
save_tracing_data: bool = False,
|
|
421
|
+
upload_inline_data_to_tos: bool = False,
|
|
422
|
+
run_processor: "BaseRunProcessor | None" = None,
|
|
423
|
+
):
|
|
424
|
+
"""Run a conversation with multi-turn text and multimodal inputs.
|
|
425
|
+
|
|
426
|
+
When short-term memory is configured, a session is auto-created as needed.
|
|
427
|
+
Inputs are converted into ADK message format. If ``upload_inline_data_to_tos``
|
|
428
|
+
is ``True``, media upload is enabled temporarily for this run (does not change
|
|
429
|
+
the Runner's global setting).
|
|
430
|
+
|
|
431
|
+
Args:
|
|
432
|
+
messages (RunnerMessage): Input messages (``str``, ``MediaMessage`` or a list of them).
|
|
433
|
+
user_id (str): Override default user ID; if empty, uses the constructed ``user_id``.
|
|
434
|
+
session_id (str): Session ID. Defaults to a timestamp-based temporary ID.
|
|
435
|
+
run_config (google.adk.agents.RunConfig | None): Run config; if ``None``, a default
|
|
436
|
+
config is created using the environment var ``MODEL_AGENT_MAX_LLM_CALLS``.
|
|
437
|
+
save_tracing_data (bool): Whether to dump tracing data to disk after the run. Defaults to ``False``.
|
|
438
|
+
upload_inline_data_to_tos (bool): Whether to enable media upload only for this run. Defaults to ``False``.
|
|
439
|
+
run_processor (BaseRunProcessor | None): Optional run processor to use for this run.
|
|
440
|
+
If not provided, uses the runner's default run_processor. Defaults to None.
|
|
441
|
+
|
|
442
|
+
Returns:
|
|
443
|
+
str: The textual output from the last event, if present; otherwise an empty string.
|
|
444
|
+
|
|
445
|
+
Raises:
|
|
446
|
+
ValueError: If an input contains an unsupported or unrecognized media type.
|
|
447
|
+
AssertionError: If a media MIME type is not among ``image/*`` or ``video/*``.
|
|
448
|
+
Exception: Exceptions from the underlying ADK/Agent execution may propagate.
|
|
449
|
+
"""
|
|
450
|
+
if upload_inline_data_to_tos:
|
|
451
|
+
_upload_inline_data_to_tos = self.upload_inline_data_to_tos
|
|
452
|
+
self.upload_inline_data_to_tos = upload_inline_data_to_tos
|
|
453
|
+
|
|
454
|
+
if not run_config:
|
|
455
|
+
run_config = RunConfig(
|
|
456
|
+
# streaming_mode=stream_mode,
|
|
457
|
+
max_llm_calls=int(getenv("MODEL_AGENT_MAX_LLM_CALLS", 100)),
|
|
458
|
+
)
|
|
459
|
+
logger.info(f"Run config: {run_config}")
|
|
460
|
+
|
|
461
|
+
user_id = user_id or self.user_id
|
|
462
|
+
|
|
463
|
+
converted_messages: list = _convert_messages(
|
|
464
|
+
messages, self.app_name, user_id, session_id
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
if self.short_term_memory:
|
|
468
|
+
session = await self.short_term_memory.create_session(
|
|
469
|
+
app_name=self.app_name, user_id=user_id, session_id=session_id
|
|
470
|
+
)
|
|
471
|
+
assert session, (
|
|
472
|
+
f"Failed to create session with app_name={self.app_name}, user_id={user_id}, session_id={session_id}, "
|
|
473
|
+
)
|
|
474
|
+
logger.debug(
|
|
475
|
+
f"Auto create session: {session.id}, user_id: {session.user_id}, app_name: {self.app_name}"
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
final_output = ""
|
|
479
|
+
for converted_message in converted_messages:
|
|
480
|
+
try:
|
|
481
|
+
|
|
482
|
+
@(run_processor or self.run_processor).process_run(
|
|
483
|
+
runner=self, message=converted_message
|
|
484
|
+
)
|
|
485
|
+
async def event_generator():
|
|
486
|
+
async for event in self.run_async(
|
|
487
|
+
user_id=user_id,
|
|
488
|
+
session_id=session_id,
|
|
489
|
+
new_message=converted_message,
|
|
490
|
+
run_config=run_config,
|
|
491
|
+
):
|
|
492
|
+
yield event
|
|
493
|
+
|
|
494
|
+
async for event in event_generator():
|
|
495
|
+
if event.get_function_calls():
|
|
496
|
+
for function_call in event.get_function_calls():
|
|
497
|
+
logger.debug(f"Function call: {function_call}")
|
|
498
|
+
elif (
|
|
499
|
+
event.content is not None
|
|
500
|
+
and event.content.parts
|
|
501
|
+
and event.content.parts[0].text is not None
|
|
502
|
+
and len(event.content.parts[0].text.strip()) > 0
|
|
503
|
+
):
|
|
504
|
+
final_output = event.content.parts[0].text
|
|
505
|
+
logger.debug(f"Event output: {final_output}")
|
|
506
|
+
except LlmCallsLimitExceededError as e:
|
|
507
|
+
logger.warning(f"Max number of llm calls limit exceeded: {e}")
|
|
508
|
+
final_output = ""
|
|
509
|
+
|
|
510
|
+
# try to save tracing file
|
|
511
|
+
if save_tracing_data:
|
|
512
|
+
self.save_tracing_file(session_id)
|
|
513
|
+
|
|
514
|
+
self._print_trace_id()
|
|
515
|
+
|
|
516
|
+
if upload_inline_data_to_tos:
|
|
517
|
+
self.upload_inline_data_to_tos = _upload_inline_data_to_tos # type: ignore
|
|
518
|
+
|
|
519
|
+
return final_output
|
|
520
|
+
|
|
521
|
+
def get_trace_id(self) -> str:
|
|
522
|
+
"""Get the Trace ID from the current agent's tracer.
|
|
523
|
+
|
|
524
|
+
If the agent is not a :class:`veadk.agent.Agent` or no tracer is configured,
|
|
525
|
+
returns ``"<unknown_trace_id>"``.
|
|
526
|
+
|
|
527
|
+
Returns:
|
|
528
|
+
str: The Trace ID or ``"<unknown_trace_id>"``.
|
|
529
|
+
|
|
530
|
+
Raises:
|
|
531
|
+
None
|
|
532
|
+
"""
|
|
533
|
+
if not isinstance(self.agent, Agent):
|
|
534
|
+
logger.warning(
|
|
535
|
+
("The agent is not an instance of VeADK Agent, no trace id provided.")
|
|
536
|
+
)
|
|
537
|
+
return "<unknown_trace_id>"
|
|
538
|
+
|
|
539
|
+
if not self.agent.tracers:
|
|
540
|
+
logger.warning(
|
|
541
|
+
"No tracer is configured in the agent, no trace id provided."
|
|
542
|
+
)
|
|
543
|
+
return "<unknown_trace_id>"
|
|
544
|
+
|
|
545
|
+
try:
|
|
546
|
+
trace_id = self.agent.tracers[0].trace_id # type: ignore
|
|
547
|
+
return trace_id
|
|
548
|
+
except Exception as e:
|
|
549
|
+
logger.warning(f"Get tracer id failed as {e}")
|
|
550
|
+
return "<unknown_trace_id>"
|
|
551
|
+
|
|
552
|
+
def _print_trace_id(self) -> None:
|
|
553
|
+
"""Log the current tracer's Trace ID.
|
|
554
|
+
|
|
555
|
+
If the agent is not a :class:`veadk.agent.Agent` or no tracer is configured,
|
|
556
|
+
nothing is printed.
|
|
557
|
+
|
|
558
|
+
Returns:
|
|
559
|
+
None
|
|
560
|
+
|
|
561
|
+
Raises:
|
|
562
|
+
None
|
|
563
|
+
"""
|
|
564
|
+
if not isinstance(self.agent, Agent):
|
|
565
|
+
logger.warning(
|
|
566
|
+
("The agent is not an instance of VeADK Agent, no trace id provided.")
|
|
567
|
+
)
|
|
568
|
+
return
|
|
569
|
+
|
|
570
|
+
if not self.agent.tracers:
|
|
571
|
+
logger.warning(
|
|
572
|
+
"No tracer is configured in the agent, no trace id provided."
|
|
573
|
+
)
|
|
574
|
+
return
|
|
575
|
+
|
|
576
|
+
try:
|
|
577
|
+
trace_id = self.agent.tracers[0].trace_id # type: ignore
|
|
578
|
+
logger.info(f"Trace id: {trace_id}")
|
|
579
|
+
except Exception as e:
|
|
580
|
+
logger.warning(f"Get tracer id failed as {e}")
|
|
581
|
+
return
|
|
582
|
+
|
|
583
|
+
def save_tracing_file(self, session_id: str) -> str:
|
|
584
|
+
"""Dump tracing data to disk and return the last written path.
|
|
585
|
+
|
|
586
|
+
Only effective when the agent is one of
|
|
587
|
+
Agent/SequentialAgent/ParallelAgent/LoopAgent and a tracer is configured;
|
|
588
|
+
otherwise returns an empty string.
|
|
589
|
+
|
|
590
|
+
Args:
|
|
591
|
+
session_id (str): Session ID used to associate the tracing with a session.
|
|
592
|
+
|
|
593
|
+
Returns:
|
|
594
|
+
str: The tracing file path; returns an empty string on failure or when no tracer.
|
|
595
|
+
|
|
596
|
+
Examples:
|
|
597
|
+
You can save the tracing data to a local file.
|
|
598
|
+
|
|
599
|
+
```python
|
|
600
|
+
import asyncio
|
|
601
|
+
|
|
602
|
+
from veadk import Agent, Runner
|
|
603
|
+
|
|
604
|
+
agent = Agent()
|
|
605
|
+
|
|
606
|
+
runner = Runner(agent=agent)
|
|
607
|
+
|
|
608
|
+
session_id = "session"
|
|
609
|
+
asyncio.run(runner.run(messages="Hi!", session_id=session_id))
|
|
610
|
+
|
|
611
|
+
path = runner.save_tracing_file(session_id=session_id)
|
|
612
|
+
print(path)
|
|
613
|
+
```
|
|
614
|
+
"""
|
|
615
|
+
if not isinstance(
|
|
616
|
+
self.agent, (Agent, SequentialAgent, ParallelAgent, LoopAgent)
|
|
617
|
+
):
|
|
618
|
+
logger.warning(
|
|
619
|
+
(
|
|
620
|
+
"The agent is not an instance of Agent, SequentialAgent, ParallelAgent or LoopAgent, cannot save tracing file."
|
|
621
|
+
)
|
|
622
|
+
)
|
|
623
|
+
return ""
|
|
624
|
+
|
|
625
|
+
if not self.agent.tracers:
|
|
626
|
+
logger.warning("No tracer is configured in the agent.")
|
|
627
|
+
return ""
|
|
628
|
+
|
|
629
|
+
try:
|
|
630
|
+
dump_path = ""
|
|
631
|
+
for tracer in self.agent.tracers:
|
|
632
|
+
dump_path = tracer.dump(user_id=self.user_id, session_id=session_id)
|
|
633
|
+
|
|
634
|
+
return dump_path
|
|
635
|
+
except Exception as e:
|
|
636
|
+
logger.error(f"Failed to save tracing file: {e}")
|
|
637
|
+
return ""
|
|
638
|
+
|
|
639
|
+
async def save_eval_set(self, session_id: str, eval_set_id: str = "default") -> str:
|
|
640
|
+
"""Save the current session as part of an evaluation set and return its path.
|
|
641
|
+
|
|
642
|
+
Args:
|
|
643
|
+
session_id (str): Session ID.
|
|
644
|
+
eval_set_id (str): Evaluation set identifier. Defaults to ``"default"``.
|
|
645
|
+
|
|
646
|
+
Returns:
|
|
647
|
+
str: The exported evaluation set file path.
|
|
648
|
+
|
|
649
|
+
Examples:
|
|
650
|
+
You can save the specific session as a evaluation set in Google ADK format.
|
|
651
|
+
|
|
652
|
+
```python
|
|
653
|
+
import asyncio
|
|
654
|
+
|
|
655
|
+
from veadk import Agent, Runner
|
|
656
|
+
|
|
657
|
+
agent = Agent()
|
|
658
|
+
|
|
659
|
+
runner = Runner(agent=agent)
|
|
660
|
+
|
|
661
|
+
session_id = "session"
|
|
662
|
+
asyncio.run(runner.run(messages="Hi!", session_id=session_id))
|
|
663
|
+
|
|
664
|
+
path = runner.save_eval_set(session_id=session_id)
|
|
665
|
+
print(path)
|
|
666
|
+
```
|
|
667
|
+
"""
|
|
668
|
+
eval_set_recorder = EvalSetRecorder(self.session_service, eval_set_id)
|
|
669
|
+
eval_set_path = await eval_set_recorder.dump(
|
|
670
|
+
self.app_name, self.user_id, session_id
|
|
671
|
+
)
|
|
672
|
+
return eval_set_path
|
|
673
|
+
|
|
674
|
+
async def save_session_to_long_term_memory(
|
|
675
|
+
self, session_id: str, user_id: str = "", app_name: str = ""
|
|
676
|
+
) -> None:
|
|
677
|
+
"""Save the specified session to long-term memory.
|
|
678
|
+
|
|
679
|
+
If ``long_term_memory`` is not configured, the function logs a warning and returns.
|
|
680
|
+
It fetches the session from the session service and then calls the long-term memory's
|
|
681
|
+
``add_session_to_memory`` for persistence.
|
|
682
|
+
|
|
683
|
+
Args:
|
|
684
|
+
session_id (str): Session ID.
|
|
685
|
+
user_id (str): Optional; override default user ID. If empty, uses ``self.user_id``.
|
|
686
|
+
app_name (str): Optional; override default app name. If empty, uses ``self.app_name``.
|
|
687
|
+
|
|
688
|
+
Examples:
|
|
689
|
+
You can save a specific session to long-term memory.
|
|
690
|
+
|
|
691
|
+
```python
|
|
692
|
+
import asyncio
|
|
693
|
+
|
|
694
|
+
from veadk import Agent, Runner
|
|
695
|
+
from veadk.memory import LongTermMemory
|
|
696
|
+
|
|
697
|
+
APP_NAME = "app"
|
|
698
|
+
|
|
699
|
+
agent = Agent(long_term_memory=LongTermMemory(backend="local", app_name=APP_NAME))
|
|
700
|
+
|
|
701
|
+
session_id = "session"
|
|
702
|
+
runner = Runner(agent=agent, app_name=APP_NAME)
|
|
703
|
+
|
|
704
|
+
asyncio.run(runner.run(messages="Hi!", session_id=session_id))
|
|
705
|
+
|
|
706
|
+
asyncio.run(runner.save_session_to_long_term_memory(session_id=session_id))
|
|
707
|
+
```
|
|
708
|
+
"""
|
|
709
|
+
if not self.long_term_memory:
|
|
710
|
+
logger.warning("Long-term memory is not enabled. Failed to save session.")
|
|
711
|
+
return
|
|
712
|
+
|
|
713
|
+
if not user_id:
|
|
714
|
+
user_id = self.user_id
|
|
715
|
+
|
|
716
|
+
if not app_name:
|
|
717
|
+
app_name = self.app_name
|
|
718
|
+
|
|
719
|
+
session = await self.session_service.get_session(
|
|
720
|
+
app_name=app_name,
|
|
721
|
+
user_id=user_id,
|
|
722
|
+
session_id=session_id,
|
|
723
|
+
)
|
|
724
|
+
|
|
725
|
+
if not session:
|
|
726
|
+
logger.error(
|
|
727
|
+
f"Session {session_id} (app_name={app_name}, user_id={user_id}) not found in session service, cannot save to long-term memory."
|
|
728
|
+
)
|
|
729
|
+
return
|
|
730
|
+
|
|
731
|
+
await self.long_term_memory.add_session_to_memory(session)
|
|
732
|
+
logger.info(f"Add session `{session.id}` to long term memory.")
|