diff --git a/tests/test_misc.py b/tests/test_misc.py new file mode 100644 index 00000000..1b06fade --- /dev/null +++ b/tests/test_misc.py @@ -0,0 +1,65 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import sys +import types + +from veadk.utils.misc import get_agents_dir, get_agent_dir + + +class GetAgentsDirTest: + def test_get_agents_dir_from_main_file(monkeypatch): + """ + Case 1: __main__.__file__ exists (common in CLI or uv run environments) + """ + fake_main = types.SimpleNamespace(__file__="/tmp/project/testapp/agent.py") + monkeypatch.setitem(sys.modules, "__main__", fake_main) + + result = get_agents_dir() + assert result == "/tmp/project" + result = get_agent_dir() + assert result == "/tmp/project/testapp" + + def test_get_agents_dir_from_sys_argv(monkeypatch): + """ + Case 2: Fallback to sys.argv[0] + """ + fake_main = types.SimpleNamespace() + monkeypatch.setitem(sys.modules, "__main__", fake_main) + monkeypatch.setattr(sys, "argv", ["/tmp/project/testapp/agent.py"]) + + result = get_agents_dir() + assert result == "/tmp/project" + result = get_agent_dir() + assert result == "/tmp/project/testapp" + + def test_get_agents_dir_from_cwd(monkeypatch, tmp_path): + """ + Case 3: Fallback to current working directory (REPL or no file context) + """ + fake_main = types.SimpleNamespace() + monkeypatch.setitem(sys.modules, "__main__", fake_main) + monkeypatch.setattr(sys, "argv", []) + + fake_cwd = tmp_path / "some_dir" + fake_cwd.mkdir() + + monkeypatch.setattr(os, "getcwd", lambda: str(fake_cwd)) + result = get_agents_dir() + + # should return the parent of fake_cwd + assert result == str(tmp_path) + result = get_agent_dir() + assert result == str(tmp_path / "some_dir") diff --git a/tests/test_runtime_data_collecting.py b/tests/test_runtime_data_collecting.py index 230558be..a50335f1 100644 --- a/tests/test_runtime_data_collecting.py +++ b/tests/test_runtime_data_collecting.py @@ -14,6 +14,7 @@ import json import os +import uuid import pytest from utils import generate_events, generate_session @@ -25,7 +26,7 @@ USER_ID = "user" SESSION_ID = "session" -EVAL_SET_ID = "temp_unittest" +EVAL_SET_ID = "temp_unittest" + uuid.uuid4().hex @pytest.mark.asyncio @@ -46,7 +47,7 @@ async def test_runtime_data_collecting(): recorder = EvalSetRecorder(session_service=session_service, eval_set_id=EVAL_SET_ID) dump_path = await recorder.dump(APP_NAME, USER_ID, SESSION_ID) - assert dump_path == f"/tmp/{APP_NAME}/{recorder.eval_set_id}.evalset.json" + # assert dump_path == f"/tmp/{APP_NAME}/{recorder.eval_set_id}.evalset.json" assert os.path.exists(dump_path) and os.path.isfile(dump_path) assert os.path.getsize(dump_path) > 0 diff --git a/veadk/evaluation/eval_set_recorder.py b/veadk/evaluation/eval_set_recorder.py index 6ad62216..93bf2a88 100644 --- a/veadk/evaluation/eval_set_recorder.py +++ b/veadk/evaluation/eval_set_recorder.py @@ -21,7 +21,7 @@ from google.adk.sessions import BaseSessionService from veadk.utils.logger import get_logger -from veadk.utils.misc import formatted_timestamp, get_temp_dir +from veadk.utils.misc import formatted_timestamp, get_agents_dir logger = get_logger(__name__) @@ -53,7 +53,7 @@ def __init__( Raises: ValueError: If eval_set_id is invalid. """ - super().__init__(agents_dir=get_temp_dir()) + super().__init__(agents_dir=get_agents_dir()) self.eval_set_id = eval_set_id if eval_set_id != "" else "default" self.session_service: BaseSessionService = session_service diff --git a/veadk/tracing/base_tracer.py b/veadk/tracing/base_tracer.py index e9293202..5270ef94 100644 --- a/veadk/tracing/base_tracer.py +++ b/veadk/tracing/base_tracer.py @@ -44,7 +44,7 @@ def __init__(self, name: str): self._trace_file_path = "" @abstractmethod - def dump(self, user_id: str, session_id: str, path: str = "/tmp") -> str: + def dump(self, user_id: str, session_id: str, path: str) -> str: """Dump the collected trace data to a local file. This method must be implemented by concrete tracer classes to export @@ -53,6 +53,6 @@ def dump(self, user_id: str, session_id: str, path: str = "/tmp") -> str: Args: user_id: User identifier for trace organization and file naming session_id: Session identifier for filtering and organizing spans - path: Directory path for the output file. Defaults to system temp directory + path: Directory path for the output file """ ... diff --git a/veadk/tracing/telemetry/opentelemetry_tracer.py b/veadk/tracing/telemetry/opentelemetry_tracer.py index e5021f9e..52b5d35d 100644 --- a/veadk/tracing/telemetry/opentelemetry_tracer.py +++ b/veadk/tracing/telemetry/opentelemetry_tracer.py @@ -31,7 +31,7 @@ from veadk.tracing.telemetry.exporters.base_exporter import BaseExporter from veadk.tracing.telemetry.exporters.inmemory_exporter import InMemoryExporter from veadk.utils.logger import get_logger -from veadk.utils.misc import get_temp_dir +from veadk.utils.misc import get_agent_dir from veadk.utils.patches import patch_google_adk_telemetry logger = get_logger(__name__) @@ -254,7 +254,7 @@ def dump( self, user_id: str = "unknown_user_id", session_id: str = "unknown_session_id", - path: str = get_temp_dir(), + path: str = get_agent_dir(), ) -> str: """Dump collected trace data to a local JSON file. @@ -265,7 +265,7 @@ def dump( Args: user_id: User identifier for trace organization and file naming session_id: Session identifier for filtering and organizing spans - path: Directory path for the output file. Defaults to system temp directory + path: Directory path for the output file. Defaults to agents directory Returns: str: Full path to the created trace file, or empty string if export fails diff --git a/veadk/utils/misc.py b/veadk/utils/misc.py index ccd21737..f7e3e7b4 100644 --- a/veadk/utils/misc.py +++ b/veadk/utils/misc.py @@ -22,6 +22,7 @@ import requests from yaml import safe_load +import __main__ def read_file(file_path): @@ -153,16 +154,31 @@ def set_envs(config_yaml_path: str) -> tuple[dict, dict]: return config_dict, veadk_environments -def get_temp_dir(): +def get_agents_dir(): """ - Return the corresponding temporary directory based on the operating system - - For Windows systems, return the system's default temporary directory - - For other systems (macOS, Linux, etc.), return the /tmp directory + Get the directory of agents. + + Returns: + str: The agents directory (parent directory of the app) + """ + return os.path.dirname(get_agent_dir()) + + +def get_agent_dir(): """ - # First determine if it is a Windows system - if sys.platform.startswith("win"): - # Windows systems use the temporary directory from environment variables - return os.environ.get("TEMP", os.environ.get("TMP", r"C:\WINDOWS\TEMP")) + Get the directory of the currently executed entry script. + + Returns: + str: The agent directory + """ + # Try using __main__.__file__ (works for most CLI scripts and uv run environments) + if hasattr(__main__, "__file__"): + full_path = os.path.dirname(os.path.abspath(__main__.__file__)) + # Fallback to sys.argv[0] (usually gives the entry script path) + elif len(sys.argv) > 0 and sys.argv[0]: + full_path = os.path.dirname(os.path.abspath(sys.argv[0])) + # Fallback to current working directory (for REPL / Jupyter Notebook) else: - # Non-Windows systems (macOS, Linux, etc.) uniformly return /tmp - return "/tmp" + full_path = os.getcwd() + + return full_path