From 38f133ed628f72c3735b4905f67397aa7de63ed9 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Fri, 26 Sep 2025 14:04:42 +0800 Subject: [PATCH 01/18] feat: add diff-cover to development dependencies and update linting configuration --- python/third_party/ai-hedge-fund/uv.lock | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/python/third_party/ai-hedge-fund/uv.lock b/python/third_party/ai-hedge-fund/uv.lock index 3d8de0abe..a2122d0cc 100644 --- a/python/third_party/ai-hedge-fund/uv.lock +++ b/python/third_party/ai-hedge-fund/uv.lock @@ -3115,6 +3115,7 @@ requires-dist = [ { name = "agno", extras = ["openai"], specifier = ">=1.8.2,<2.0" }, { name = "aiosqlite", specifier = ">=0.19.0" }, { name = "akshare", specifier = ">=1.17.44" }, + { name = "diff-cover", marker = "extra == 'dev'", specifier = ">=9.0.0" }, { name = "edgartools", specifier = ">=4.12.2" }, { name = "fastapi", specifier = ">=0.104.0" }, { name = "pydantic", specifier = ">=2.0.0" }, @@ -3134,13 +3135,20 @@ provides-extras = ["dev"] [package.metadata.requires-dev] dev = [ + { name = "diff-cover", specifier = ">=9.0.0" }, + { name = "isort" }, { name = "pytest", specifier = ">=7.4.0" }, { name = "pytest-asyncio", specifier = ">=1.0.0" }, { name = "pytest-cov", specifier = ">=4.1.0" }, { name = "ruff" }, ] lint = [{ name = "ruff" }] +style = [ + { name = "isort" }, + { name = "ruff" }, +] test = [ + { name = "diff-cover", specifier = ">=9.0.0" }, { name = "pytest", specifier = ">=7.4.0" }, { name = "pytest-asyncio", specifier = ">=1.0.0" }, { name = "pytest-cov", specifier = ">=4.1.0" }, From 003816752f5ff282cc8fd60389f25d91e72c1031 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Fri, 26 Sep 2025 14:30:54 +0800 Subject: [PATCH 02/18] refactor: remove auto-import functionality for agents in __init__.py --- python/valuecell/agents/__init__.py | 61 ----------------------------- 1 file changed, 61 deletions(-) diff --git a/python/valuecell/agents/__init__.py b/python/valuecell/agents/__init__.py index 2be9c8022..e69de29bb 100644 --- a/python/valuecell/agents/__init__.py +++ b/python/valuecell/agents/__init__.py @@ -1,61 +0,0 @@ -""" -Auto-import all agents to ensure they are registered with the AgentRegistry. -This module dynamically discovers and imports all agent classes. -""" - -import importlib -import inspect -import pkgutil -from pathlib import Path -from typing import List - -from valuecell.core.types import BaseAgent - - -def _discover_and_import_agents() -> List[str]: - """ - Dynamically discover and import all agent modules in this package. - - Returns: - List of agent class names that were imported - """ - imported_agents = [] - current_package = __name__ - current_path = Path(__file__).parent - - # Iterate through all Python files in the current directory - for _, module_name, _ in pkgutil.iter_modules([str(current_path)]): - if module_name.startswith("_"): - # Skip private modules - continue - - try: - # Import the module - module = importlib.import_module(f"{current_package}.{module_name}") - - # Find all classes in the module that inherit from BaseAgent - for name, obj in inspect.getmembers(module, inspect.isclass): - if ( - obj.__module__ == module.__name__ - and issubclass(obj, BaseAgent) - and obj != BaseAgent - ): - imported_agents.append(name) - # Make the class available at package level - globals()[name] = obj - - except Exception as e: - # Log import errors but continue with other modules - import logging - - logger = logging.getLogger(__name__) - logger.warning(f"Failed to import module {module_name}: {e}") - - return imported_agents - - -# Auto-import all agents -_imported_agent_names = _discover_and_import_agents() - -# Export all discovered agents for convenient access -__all__ = _imported_agent_names From 2fe71419cb409876d2427fbbb625a8192c5261cb Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Fri, 26 Sep 2025 14:38:48 +0800 Subject: [PATCH 03/18] feat: update uv.lock to include aiosqlite and diff-cover dependencies --- python/third_party/TradingAgents/uv.lock | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/python/third_party/TradingAgents/uv.lock b/python/third_party/TradingAgents/uv.lock index bc2aebd29..56407665c 100644 --- a/python/third_party/TradingAgents/uv.lock +++ b/python/third_party/TradingAgents/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.12" resolution-markers = [ "python_full_version >= '3.13'", @@ -140,6 +140,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, ] +[[package]] +name = "aiosqlite" +version = "0.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/13/7d/8bca2bf9a247c2c5dfeec1d7a5f40db6518f88d314b8bca9da29670d2671/aiosqlite-0.21.0.tar.gz", hash = "sha256:131bb8056daa3bc875608c631c678cda73922a2d4ba8aec373b19f18c17e7aa3", size = 13454, upload-time = "2025-02-03T07:30:16.235Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/10/6c25ed6de94c49f88a91fa5018cb4c0f3625f31d5be9f771ebe5cc7cd506/aiosqlite-0.21.0-py3-none-any.whl", hash = "sha256:2549cf4057f95f53dcba16f2b64e8e2791d7e1adedb13197dd8ed77bb226d7d0", size = 15792, upload-time = "2025-02-03T07:30:13.6Z" }, +] + [[package]] name = "akracer" version = "0.0.14" @@ -4764,6 +4776,7 @@ source = { editable = "../../" } dependencies = [ { name = "a2a-sdk", extra = ["http-server"] }, { name = "agno", extra = ["openai"] }, + { name = "aiosqlite" }, { name = "akshare" }, { name = "edgartools" }, { name = "fastapi" }, @@ -4781,7 +4794,9 @@ dependencies = [ requires-dist = [ { name = "a2a-sdk", extras = ["http-server"], specifier = ">=0.3.4" }, { name = "agno", extras = ["openai"], specifier = ">=1.8.2,<2.0" }, + { name = "aiosqlite", specifier = ">=0.19.0" }, { name = "akshare", specifier = ">=1.17.44" }, + { name = "diff-cover", marker = "extra == 'dev'", specifier = ">=9.0.0" }, { name = "edgartools", specifier = ">=4.12.2" }, { name = "fastapi", specifier = ">=0.104.0" }, { name = "pydantic", specifier = ">=2.0.0" }, @@ -4801,13 +4816,20 @@ provides-extras = ["dev"] [package.metadata.requires-dev] dev = [ + { name = "diff-cover", specifier = ">=9.0.0" }, + { name = "isort" }, { name = "pytest", specifier = ">=7.4.0" }, { name = "pytest-asyncio", specifier = ">=1.0.0" }, { name = "pytest-cov", specifier = ">=4.1.0" }, { name = "ruff" }, ] lint = [{ name = "ruff" }] +style = [ + { name = "isort" }, + { name = "ruff" }, +] test = [ + { name = "diff-cover", specifier = ">=9.0.0" }, { name = "pytest", specifier = ">=7.4.0" }, { name = "pytest-asyncio", specifier = ">=1.0.0" }, { name = "pytest-cov", specifier = ">=4.1.0" }, From c9ee9c9e0dba125b48391de97d836a6d57cd8281 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Fri, 26 Sep 2025 14:38:57 +0800 Subject: [PATCH 04/18] feat: add error handling for agent card resolution in AgentClient --- python/valuecell/core/agent/client.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/python/valuecell/core/agent/client.py b/python/valuecell/core/agent/client.py index 6e93f5981..d333834eb 100644 --- a/python/valuecell/core/agent/client.py +++ b/python/valuecell/core/agent/client.py @@ -60,7 +60,13 @@ async def _setup_client(self): client_factory = ClientFactory(config) card_resolver = A2ACardResolver(self._httpx_client, self.agent_url) - self.agent_card = await card_resolver.get_agent_card() + try: + self.agent_card = await card_resolver.get_agent_card() + except Exception as e: + raise RuntimeError( + "Failed to resolve agent card. Maybe the agent URL is incorrect or the agent is unreachable." + " Agents could be launched via `scripts/launch_agent.py`." + ) from e self._client = client_factory.create(self.agent_card) async def send_message( From b41296ee77bece3ae597a2ae71533ab5062587fa Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Fri, 26 Sep 2025 14:39:05 +0800 Subject: [PATCH 05/18] feat: implement interactive agent launcher script with logging --- scripts/launch_agent.py | 105 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 scripts/launch_agent.py diff --git a/scripts/launch_agent.py b/scripts/launch_agent.py new file mode 100644 index 000000000..605b0dbff --- /dev/null +++ b/scripts/launch_agent.py @@ -0,0 +1,105 @@ +""" +Interactive agent launcher script. +Allows users to select an agent from available options and launch it using uv. +""" + +import os +from pathlib import Path +import subprocess +from datetime import datetime +from typing import Dict + +import questionary + +# Mapping from agent name to analyst key (for ai-hedge-fund agents) +MAP_NAME_ANALYST: Dict[str, str] = { + "AswathDamodaranAgent": "aswath_damodaran", + "BenGrahamAgent": "ben_graham", + "BillAckmanAgent": "bill_ackman", + "CathieWoodAgent": "cathie_wood", + "CharlieMungerAgent": "charlie_munger", + "FundamentalsAnalystAgent": "fundamentals_analyst", + "MichaelBurryAgent": "michael_burry", + "MohnishPabraiAgent": "mohnish_pabrai", + "PeterLynchAgent": "peter_lynch", + "PhilFisherAgent": "phil_fisher", + "RakeshJhunjhunwalaAgent": "rakesh_jhunjhunwala", + "SentimentAnalystAgent": "sentiment_analyst", + "StanleyDruckenmillerAgent": "stanley_druckenmiller", + "TechnicalAnalystAgent": "technical_analyst", + "ValuationAnalystAgent": "valuation_analyst", + "WarrenBuffettAgent": "warren_buffett", +} +SEC_AGENT_NAME = "SecAgent" +TRADING_AGENTS_NAME = "TradingAgentsAdapter" +PROJECT_DIR = Path(__file__).resolve().parent.parent + +# Mapping from agent name to launch command +MAP_NAME_COMMAND: Dict[str, str] = {} +for name, analyst in MAP_NAME_ANALYST.items(): + MAP_NAME_COMMAND[name] = ( + f"cd {PROJECT_DIR}/python/third_party/ai-hedge-fund && uv run python -m adapter --analyst {analyst}" + ) +MAP_NAME_COMMAND[SEC_AGENT_NAME] = "uv run -m valuecell.agents.sec_agent" +MAP_NAME_COMMAND[TRADING_AGENTS_NAME] = ( + f"cd {PROJECT_DIR}/python/third_party/TradingAgents && uv run -m adapter" +) + + +def check_envfile_is_set(): + env_path = PROJECT_DIR.parent / ".env" + if not env_path.exists(): + print( + f".env file not found at {env_path}. Please create it with necessary environment variables. " + "check python/.env.example for reference." + ) + exit(1) + + +def main(): + check_envfile_is_set() + timestamp = datetime.now().strftime("%Y%m%d%H%M%S") + log_dir = f"logs/{timestamp}" + agents = list(MAP_NAME_COMMAND.keys()) + + # Use questionary multi-select to allow choosing multiple agents + selected_agents = questionary.checkbox( + "Choose agents to launch (use space to select, enter to confirm):", + choices=agents, + ).ask() + + if selected_agents: + os.makedirs(log_dir, exist_ok=True) + print(f"Logs will be saved to {log_dir}/") + + processes = [] + logfiles = [] + for selected_agent in selected_agents: + logfile_path = f"{log_dir}/{selected_agent}.log" + print(f"Starting agent: {selected_agent} - output to {logfile_path}") + + # Open logfile for writing + logfile = open(logfile_path, "w") + logfiles.append(logfile) + + # Launch command using Popen with output redirected to logfile + launch_command = MAP_NAME_COMMAND[selected_agent] + process = subprocess.Popen( + launch_command, shell=True, stdout=logfile, stderr=logfile + ) + processes.append(process) + + # Wait for all processes to complete + print("All agents launched. Waiting for tasks...") + for process in processes: + process.wait() + # Close all logfiles + for logfile in logfiles: + logfile.close() + print(f"All agents finished. Check {log_dir}/ for output.") + else: + print("No agents selected.") + + +if __name__ == "__main__": + main() From e57bba8d88e797b3ba85f0c7eecc5763813e0f7a Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:00:41 +0800 Subject: [PATCH 06/18] feat: add interactive agent launcher and environment preparation scripts --- {scripts => python/scripts}/launch_agent.py | 79 ++++++++++++--------- python/scripts/prepare_envs.sh | 57 +++++++++++++++ 2 files changed, 102 insertions(+), 34 deletions(-) rename {scripts => python/scripts}/launch_agent.py (56%) create mode 100644 python/scripts/prepare_envs.sh diff --git a/scripts/launch_agent.py b/python/scripts/launch_agent.py similarity index 56% rename from scripts/launch_agent.py rename to python/scripts/launch_agent.py index 605b0dbff..0c78630fb 100644 --- a/scripts/launch_agent.py +++ b/python/scripts/launch_agent.py @@ -32,22 +32,23 @@ } SEC_AGENT_NAME = "SecAgent" TRADING_AGENTS_NAME = "TradingAgentsAdapter" -PROJECT_DIR = Path(__file__).resolve().parent.parent +PROJECT_DIR = Path(__file__).resolve().parent.parent.parent +PYTHON_DIR = PROJECT_DIR / "python" # Mapping from agent name to launch command MAP_NAME_COMMAND: Dict[str, str] = {} for name, analyst in MAP_NAME_ANALYST.items(): MAP_NAME_COMMAND[name] = ( - f"cd {PROJECT_DIR}/python/third_party/ai-hedge-fund && uv run python -m adapter --analyst {analyst}" + f"cd {PYTHON_DIR}/third_party/ai-hedge-fund && uv run python -m adapter --analyst {analyst}" ) MAP_NAME_COMMAND[SEC_AGENT_NAME] = "uv run -m valuecell.agents.sec_agent" MAP_NAME_COMMAND[TRADING_AGENTS_NAME] = ( - f"cd {PROJECT_DIR}/python/third_party/TradingAgents && uv run -m adapter" + f"cd {PYTHON_DIR}/third_party/TradingAgents && uv run -m adapter" ) def check_envfile_is_set(): - env_path = PROJECT_DIR.parent / ".env" + env_path = PROJECT_DIR / ".env" if not env_path.exists(): print( f".env file not found at {env_path}. Please create it with necessary environment variables. " @@ -68,37 +69,47 @@ def main(): choices=agents, ).ask() - if selected_agents: - os.makedirs(log_dir, exist_ok=True) - print(f"Logs will be saved to {log_dir}/") - - processes = [] - logfiles = [] - for selected_agent in selected_agents: - logfile_path = f"{log_dir}/{selected_agent}.log" - print(f"Starting agent: {selected_agent} - output to {logfile_path}") - - # Open logfile for writing - logfile = open(logfile_path, "w") - logfiles.append(logfile) - - # Launch command using Popen with output redirected to logfile - launch_command = MAP_NAME_COMMAND[selected_agent] - process = subprocess.Popen( - launch_command, shell=True, stdout=logfile, stderr=logfile - ) - processes.append(process) - - # Wait for all processes to complete - print("All agents launched. Waiting for tasks...") - for process in processes: - process.wait() - # Close all logfiles - for logfile in logfiles: - logfile.close() - print(f"All agents finished. Check {log_dir}/ for output.") - else: + if not selected_agents: print("No agents selected.") + exit(0) + + os.makedirs(log_dir, exist_ok=True) + print(f"Logs will be saved to {log_dir}/") + + processes = [] + logfiles = [] + for selected_agent in selected_agents: + logfile_path = f"{log_dir}/{selected_agent}.log" + print(f"Starting agent: {selected_agent} - output to {logfile_path}") + + # Open logfile for writing + logfile = open(logfile_path, "w") + logfiles.append(logfile) + + # Launch command using Popen with output redirected to logfile + launch_command = MAP_NAME_COMMAND[selected_agent] + process = subprocess.Popen( + launch_command, shell=True, stdout=logfile, stderr=logfile + ) + processes.append(process) + print("All agents launched. Waiting for tasks...") + + # Launch backend + logfile_path = f"{log_dir}/backend.log" + print(f"Starting backend - output to {logfile_path}") + logfile = open(logfile_path, "w") + logfiles.append(logfile) + launch_command = f"cd {PYTHON_DIR} && uv run -m valuecell.server.main" + process = subprocess.Popen( + launch_command, shell=True, stdout=logfile, stderr=logfile + ) + processes.append(process) + + for process in processes: + process.wait() + for logfile in logfiles: + logfile.close() + print(f"All agents finished. Check {log_dir}/ for output.") if __name__ == "__main__": diff --git a/python/scripts/prepare_envs.sh b/python/scripts/prepare_envs.sh new file mode 100644 index 000000000..fc4203a74 --- /dev/null +++ b/python/scripts/prepare_envs.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +# Color codes for output highlighting +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Function to print highlighted command +highlight_command() { + echo -e "${BLUE}Running: $1${NC}" +} + +# Check if current directory is project root +if [ ! -f ".gitignore" ] || [ ! -d "python" ] || [ ! -d "python/third_party" ]; then + echo -e "${RED}Error: This script must be run from the project root directory.${NC}" + exit 1 +fi + +# Check if uv is installed +if ! command -v uv &> /dev/null; then + echo -e "${RED}Error: 'uv' command not found. Please install 'uv' (e.g., brew install uv).${NC}" + exit 1 +fi + +# Prepare environments +echo -e "${GREEN}Project root confirmed. Preparing environments...${NC}" + +echo -e "${YELLOW}Setting up main Python environment...${NC}" +pushd ./python +highlight_command "uv venv --python 3.12" +uv venv --python 3.12 +highlight_command "uv sync --group dev" +uv sync --group dev +popd +echo -e "${GREEN}Main environment setup complete.${NC}" + +echo -e "${YELLOW}Setting up third-party environments...${NC}" +echo -e "${YELLOW}Setting up ai-hedge-fund environment...${NC}" +pushd ./python/third_party/ai-hedge-fund +highlight_command "uv venv --python 3.12" +uv venv --python 3.12 +highlight_command "uv sync" +uv sync +popd +echo -e "${GREEN}ai-hedge-fund environment setup complete.${NC}" + +echo -e "${YELLOW}Setting up TradingAgents environment...${NC}" +pushd ./python/third_party/TradingAgents +highlight_command "uv venv --python 3.12" +uv venv --python 3.12 +highlight_command "uv sync" +uv sync +popd +echo -e "${GREEN}TradingAgents environment setup complete.${NC}" +echo -e "${GREEN}All environments are set up.${NC}" \ No newline at end of file From 200bc366077d94e536cf3162a208517be93c359b Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:02:34 +0800 Subject: [PATCH 07/18] test: add unit test for ensure_initialized card resolution failure --- .../valuecell/core/agent/tests/test_client.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/python/valuecell/core/agent/tests/test_client.py b/python/valuecell/core/agent/tests/test_client.py index 22a5b3617..6a79ce5de 100644 --- a/python/valuecell/core/agent/tests/test_client.py +++ b/python/valuecell/core/agent/tests/test_client.py @@ -262,3 +262,22 @@ async def test_close_closes_httpx_and_resets_state(self): assert client._httpx_client is None assert client._client is None assert client._initialized is False + + @pytest.mark.asyncio + async def test_ensure_initialized_card_resolution_failure(self): + """Test that ensure_initialized raises RuntimeError with helpful message on card resolution failure.""" + client = AgentClient("http://invalid-url.com") + + with patch('valuecell.core.agent.client.A2ACardResolver') as mock_resolver_class, \ + patch('httpx.AsyncClient'): + + mock_resolver = mock_resolver_class.return_value + mock_resolver.get_agent_card = AsyncMock(side_effect=Exception("Connection timeout")) + + with pytest.raises(RuntimeError) as exc_info: + await client.ensure_initialized() + + error_message = str(exc_info.value) + assert "Failed to resolve agent card" in error_message + assert "scripts/launch_agent.py" in error_message + assert "Connection timeout" in str(exc_info.value.__cause__) # Original exception should be chained From c76ce4492973565b9123de33a9b0cce5d36cf829 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:07:23 +0800 Subject: [PATCH 08/18] fix: update environment preparation script to check for project structure and improve output formatting --- python/scripts/prepare_envs.sh | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/python/scripts/prepare_envs.sh b/python/scripts/prepare_envs.sh index fc4203a74..025d6e052 100644 --- a/python/scripts/prepare_envs.sh +++ b/python/scripts/prepare_envs.sh @@ -13,8 +13,8 @@ highlight_command() { } # Check if current directory is project root -if [ ! -f ".gitignore" ] || [ ! -d "python" ] || [ ! -d "python/third_party" ]; then - echo -e "${RED}Error: This script must be run from the project root directory.${NC}" +if [ ! -f "pyproject.toml" ] || [ ! -d "third_party" ]; then + echo -e "${RED}Error: This script must be run from the project python directory.${NC}" exit 1 fi @@ -24,21 +24,25 @@ if ! command -v uv &> /dev/null; then exit 1 fi +echo -e "${BLUE}==========================================${NC}" +echo -e "${BLUE}Starting environment preparation...${NC}" +echo -e "${BLUE}==========================================${NC}" + # Prepare environments echo -e "${GREEN}Project root confirmed. Preparing environments...${NC}" echo -e "${YELLOW}Setting up main Python environment...${NC}" -pushd ./python highlight_command "uv venv --python 3.12" uv venv --python 3.12 highlight_command "uv sync --group dev" uv sync --group dev -popd echo -e "${GREEN}Main environment setup complete.${NC}" -echo -e "${YELLOW}Setting up third-party environments...${NC}" +echo -e "${BLUE}==========================================${NC}" +echo -e "${BLUE}Setting up third-party environments...${NC}" +echo -e "${BLUE}==========================================${NC}" echo -e "${YELLOW}Setting up ai-hedge-fund environment...${NC}" -pushd ./python/third_party/ai-hedge-fund +pushd ./third_party/ai-hedge-fund highlight_command "uv venv --python 3.12" uv venv --python 3.12 highlight_command "uv sync" @@ -46,12 +50,17 @@ uv sync popd echo -e "${GREEN}ai-hedge-fund environment setup complete.${NC}" +echo -e "${YELLOW}------------------------------------------${NC}" echo -e "${YELLOW}Setting up TradingAgents environment...${NC}" -pushd ./python/third_party/TradingAgents +echo -e "${YELLOW}------------------------------------------${NC}" +pushd ./third_party/TradingAgents highlight_command "uv venv --python 3.12" uv venv --python 3.12 highlight_command "uv sync" uv sync popd echo -e "${GREEN}TradingAgents environment setup complete.${NC}" -echo -e "${GREEN}All environments are set up.${NC}" \ No newline at end of file + +echo -e "${GREEN}==========================================${NC}" +echo -e "${GREEN}All environments are set up.${NC}" +echo -e "${GREEN}==========================================${NC}" \ No newline at end of file From 9f192055f2a65357ccba7f37e5dea477eb143ebf Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:11:52 +0800 Subject: [PATCH 09/18] fix: improve project root detection and error messaging in environment preparation script --- python/scripts/prepare_envs.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/python/scripts/prepare_envs.sh b/python/scripts/prepare_envs.sh index 025d6e052..9a6d20411 100644 --- a/python/scripts/prepare_envs.sh +++ b/python/scripts/prepare_envs.sh @@ -12,9 +12,18 @@ highlight_command() { echo -e "${BLUE}Running: $1${NC}" } -# Check if current directory is project root +# Check current directory and switch to python if needed +if [ -d "python" ] && [ -f "python/pyproject.toml" ] && [ -f ".gitignore" ]; then + echo -e "${YELLOW}Detected project root. Switching to python directory...${NC}" + cd python +elif [ ! -f "pyproject.toml" ] || [ ! -d "third_party" ]; then + echo -e "${RED}Error: This script must be run from the project python directory or project root. You are in $(pwd)${NC}" + exit 1 +fi + +# Final check if in python directory if [ ! -f "pyproject.toml" ] || [ ! -d "third_party" ]; then - echo -e "${RED}Error: This script must be run from the project python directory.${NC}" + echo -e "${RED}Error: Failed to switch to python directory. You are in $(pwd)${NC}" exit 1 fi From 80623c0ddbdb2223cc88f73a9170c4e4a66a2b9f Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:22:24 +0800 Subject: [PATCH 10/18] fix: refactor environment file path handling in check_envfile_is_set function --- python/scripts/launch_agent.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/scripts/launch_agent.py b/python/scripts/launch_agent.py index 0c78630fb..482151628 100644 --- a/python/scripts/launch_agent.py +++ b/python/scripts/launch_agent.py @@ -45,13 +45,13 @@ MAP_NAME_COMMAND[TRADING_AGENTS_NAME] = ( f"cd {PYTHON_DIR}/third_party/TradingAgents && uv run -m adapter" ) +ENV_PATH = PROJECT_DIR / ".env" def check_envfile_is_set(): - env_path = PROJECT_DIR / ".env" - if not env_path.exists(): + if not ENV_PATH.exists(): print( - f".env file not found at {env_path}. Please create it with necessary environment variables. " + f".env file not found at {ENV_PATH}. Please create it with necessary environment variables. " "check python/.env.example for reference." ) exit(1) From 55112a2ccd490492857abac21554924b182f5a10 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:29:29 +0800 Subject: [PATCH 11/18] feat: rename launch script --- python/scripts/{launch_agent.py => launch.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename python/scripts/{launch_agent.py => launch.py} (100%) diff --git a/python/scripts/launch_agent.py b/python/scripts/launch.py similarity index 100% rename from python/scripts/launch_agent.py rename to python/scripts/launch.py From e3bd29c26b935020c5895b03cc3ee3fac97062c9 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:48:50 +0800 Subject: [PATCH 12/18] fix: update launch commands to include environment file and improve logging output --- python/scripts/launch.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/python/scripts/launch.py b/python/scripts/launch.py index 482151628..abca8108e 100644 --- a/python/scripts/launch.py +++ b/python/scripts/launch.py @@ -34,18 +34,24 @@ TRADING_AGENTS_NAME = "TradingAgentsAdapter" PROJECT_DIR = Path(__file__).resolve().parent.parent.parent PYTHON_DIR = PROJECT_DIR / "python" +ENV_PATH = PROJECT_DIR / ".env" +ENV_PATH_STR = str(ENV_PATH.resolve()) # Mapping from agent name to launch command MAP_NAME_COMMAND: Dict[str, str] = {} for name, analyst in MAP_NAME_ANALYST.items(): MAP_NAME_COMMAND[name] = ( - f"cd {PYTHON_DIR}/third_party/ai-hedge-fund && uv run python -m adapter --analyst {analyst}" + f"cd {PYTHON_DIR}/third_party/ai-hedge-fund && uv run --env-file {ENV_PATH} -m adapter --analyst {analyst}" ) -MAP_NAME_COMMAND[SEC_AGENT_NAME] = "uv run -m valuecell.agents.sec_agent" +MAP_NAME_COMMAND[SEC_AGENT_NAME] = ( + f"uv run --env-file {ENV_PATH} -m valuecell.agents.sec_agent" +) MAP_NAME_COMMAND[TRADING_AGENTS_NAME] = ( - f"cd {PYTHON_DIR}/third_party/TradingAgents && uv run -m adapter" + f"cd {PYTHON_DIR}/third_party/TradingAgents && uv run --env-file {ENV_PATH} -m adapter" +) +BACKEND_COMMAND = ( + f"cd {PYTHON_DIR} && uv run --env-file {ENV_PATH} -m valuecell.server.main" ) -ENV_PATH = PROJECT_DIR / ".env" def check_envfile_is_set(): @@ -60,7 +66,7 @@ def check_envfile_is_set(): def main(): check_envfile_is_set() timestamp = datetime.now().strftime("%Y%m%d%H%M%S") - log_dir = f"logs/{timestamp}" + log_dir = f"{PROJECT_DIR}/logs/{timestamp}" agents = list(MAP_NAME_COMMAND.keys()) # Use questionary multi-select to allow choosing multiple agents @@ -80,16 +86,15 @@ def main(): logfiles = [] for selected_agent in selected_agents: logfile_path = f"{log_dir}/{selected_agent}.log" - print(f"Starting agent: {selected_agent} - output to {logfile_path}") + print(f"Starting agent: {selected_agent} - output to {logfile_path} - regarding env: {ENV_PATH_STR}") # Open logfile for writing logfile = open(logfile_path, "w") logfiles.append(logfile) # Launch command using Popen with output redirected to logfile - launch_command = MAP_NAME_COMMAND[selected_agent] process = subprocess.Popen( - launch_command, shell=True, stdout=logfile, stderr=logfile + MAP_NAME_COMMAND[selected_agent], shell=True, stdout=logfile, stderr=logfile ) processes.append(process) print("All agents launched. Waiting for tasks...") @@ -99,9 +104,8 @@ def main(): print(f"Starting backend - output to {logfile_path}") logfile = open(logfile_path, "w") logfiles.append(logfile) - launch_command = f"cd {PYTHON_DIR} && uv run -m valuecell.server.main" process = subprocess.Popen( - launch_command, shell=True, stdout=logfile, stderr=logfile + BACKEND_COMMAND, shell=True, stdout=logfile, stderr=logfile ) processes.append(process) From 9295b729f187f16d327a91681755a9af9da15e78 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:49:00 +0800 Subject: [PATCH 13/18] refactor: update start script to improve dependency installation and backend startup process --- start.sh | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/start.sh b/start.sh index c6c8489bc..80cc5ae80 100755 --- a/start.sh +++ b/start.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/bash set -Eeuo pipefail # Simple project launcher with auto-install for bun and uv @@ -76,11 +76,11 @@ ensure_tool() { fi } -install_dependencies() { +compile() { # Backend deps if [[ -d "$PY_DIR" ]]; then info "Sync Python dependencies (uv sync)..." - (cd "$PY_DIR" && uv sync) + (cd "$PY_DIR" && bash scripts/prepare_envs.sh) success "Python dependencies synced" else warn "Backend directory not found: $PY_DIR. Skipping" @@ -91,7 +91,7 @@ install_dependencies() { info "Install frontend dependencies (bun install)..." (cd "$FRONTEND_DIR" && bun install) success "Frontend dependencies installed" - } else { + else warn "Frontend directory not found: $FRONTEND_DIR. Skipping" fi } @@ -101,11 +101,8 @@ start_backend() { warn "Backend directory not found; skipping backend start" return 0 fi - info "Starting backend (uv run python -m valuecell.server.main)..." - ( - cd "$PY_DIR" && uv run python -m valuecell.server.main - ) & BACKEND_PID=$! - info "Backend PID: $BACKEND_PID" + info "Starting backend (uv run scripts/launch.py)..." + cd "$PY_DIR" && uv run --with questionary scripts/launch.py } start_frontend() { @@ -166,16 +163,17 @@ main() { ensure_tool bun oven-sh/bun/bun ensure_tool uv uv - install_dependencies + compile - if (( start_backend_flag )); then - start_backend - fi if (( start_frontend_flag )); then start_frontend fi + sleep 5 # Give frontend a moment to start + + if (( start_backend_flag )); then + start_backend + fi - info "Services started. Press Ctrl+C to stop." # Wait for background jobs wait } From 56a29fe4fbd3af734317b90df4cac7ef37c39d6e Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:54:05 +0800 Subject: [PATCH 14/18] feat: update agent selection to include all agents and improve logging output --- python/scripts/launch.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/python/scripts/launch.py b/python/scripts/launch.py index abca8108e..a9c91e858 100644 --- a/python/scripts/launch.py +++ b/python/scripts/launch.py @@ -32,6 +32,8 @@ } SEC_AGENT_NAME = "SecAgent" TRADING_AGENTS_NAME = "TradingAgentsAdapter" +AGENTS = list(MAP_NAME_ANALYST.keys()) + [SEC_AGENT_NAME, TRADING_AGENTS_NAME] + PROJECT_DIR = Path(__file__).resolve().parent.parent.parent PYTHON_DIR = PROJECT_DIR / "python" ENV_PATH = PROJECT_DIR / ".env" @@ -67,12 +69,11 @@ def main(): check_envfile_is_set() timestamp = datetime.now().strftime("%Y%m%d%H%M%S") log_dir = f"{PROJECT_DIR}/logs/{timestamp}" - agents = list(MAP_NAME_COMMAND.keys()) # Use questionary multi-select to allow choosing multiple agents selected_agents = questionary.checkbox( "Choose agents to launch (use space to select, enter to confirm):", - choices=agents, + choices=AGENTS, ).ask() if not selected_agents: @@ -86,7 +87,7 @@ def main(): logfiles = [] for selected_agent in selected_agents: logfile_path = f"{log_dir}/{selected_agent}.log" - print(f"Starting agent: {selected_agent} - output to {logfile_path} - regarding env: {ENV_PATH_STR}") + print(f"Starting agent: {selected_agent} - output to {logfile_path}") # Open logfile for writing logfile = open(logfile_path, "w") @@ -99,6 +100,11 @@ def main(): processes.append(process) print("All agents launched. Waiting for tasks...") + for selected_agent in selected_agents: + print( + f"You can monitor {selected_agent} logs at {log_dir}/{selected_agent}.log or chat using: $frontend_url/agent/{selected_agent}" + ) + # Launch backend logfile_path = f"{log_dir}/backend.log" print(f"Starting backend - output to {logfile_path}") From 5369db50c285cd1c3a144e1037a93658fdd548b3 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Fri, 26 Sep 2025 16:16:48 +0800 Subject: [PATCH 15/18] fix: update frontend URL reference in log messages --- python/scripts/launch.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/scripts/launch.py b/python/scripts/launch.py index a9c91e858..6660c7fd8 100644 --- a/python/scripts/launch.py +++ b/python/scripts/launch.py @@ -54,6 +54,7 @@ BACKEND_COMMAND = ( f"cd {PYTHON_DIR} && uv run --env-file {ENV_PATH} -m valuecell.server.main" ) +FRONTEND_URL = "http://localhost:1420" def check_envfile_is_set(): @@ -102,12 +103,13 @@ def main(): for selected_agent in selected_agents: print( - f"You can monitor {selected_agent} logs at {log_dir}/{selected_agent}.log or chat using: $frontend_url/agent/{selected_agent}" + f"You can monitor {selected_agent} logs at {log_dir}/{selected_agent}.log or chat on: {FRONTEND_URL}/agent/{selected_agent}" ) # Launch backend logfile_path = f"{log_dir}/backend.log" print(f"Starting backend - output to {logfile_path}") + print(f"Frontend available at {FRONTEND_URL}") logfile = open(logfile_path, "w") logfiles.append(logfile) process = subprocess.Popen( From a3ba7f0887e704315bea525dba09bd405ddd8e38 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Fri, 26 Sep 2025 16:16:55 +0800 Subject: [PATCH 16/18] feat: sync Python dependencies and initialize database during compilation --- start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/start.sh b/start.sh index 80cc5ae80..5618160fd 100755 --- a/start.sh +++ b/start.sh @@ -80,7 +80,7 @@ compile() { # Backend deps if [[ -d "$PY_DIR" ]]; then info "Sync Python dependencies (uv sync)..." - (cd "$PY_DIR" && bash scripts/prepare_envs.sh) + (cd "$PY_DIR" && bash scripts/prepare_envs.sh && uv run valuecell/server/db/init_db.py) success "Python dependencies synced" else warn "Backend directory not found: $PY_DIR. Skipping" From 7dc60cc8f2850193729f0a5231df1d886ff24d20 Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Fri, 26 Sep 2025 16:19:42 +0800 Subject: [PATCH 17/18] fix: prevent redundant virtual environment creation in setup script --- python/scripts/prepare_envs.sh | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/python/scripts/prepare_envs.sh b/python/scripts/prepare_envs.sh index 9a6d20411..9f58e8358 100644 --- a/python/scripts/prepare_envs.sh +++ b/python/scripts/prepare_envs.sh @@ -41,8 +41,12 @@ echo -e "${BLUE}==========================================${NC}" echo -e "${GREEN}Project root confirmed. Preparing environments...${NC}" echo -e "${YELLOW}Setting up main Python environment...${NC}" -highlight_command "uv venv --python 3.12" -uv venv --python 3.12 +if [ ! -d ".venv" ]; then + highlight_command "uv venv --python 3.12" + uv venv --python 3.12 +else + echo -e "${YELLOW}.venv already exists, skipping venv creation.${NC}" +fi highlight_command "uv sync --group dev" uv sync --group dev echo -e "${GREEN}Main environment setup complete.${NC}" @@ -52,8 +56,12 @@ echo -e "${BLUE}Setting up third-party environments...${NC}" echo -e "${BLUE}==========================================${NC}" echo -e "${YELLOW}Setting up ai-hedge-fund environment...${NC}" pushd ./third_party/ai-hedge-fund -highlight_command "uv venv --python 3.12" -uv venv --python 3.12 +if [ ! -d ".venv" ]; then + highlight_command "uv venv --python 3.12" + uv venv --python 3.12 +else + echo -e "${YELLOW}.venv already exists, skipping venv creation.${NC}" +fi highlight_command "uv sync" uv sync popd @@ -63,8 +71,12 @@ echo -e "${YELLOW}------------------------------------------${NC}" echo -e "${YELLOW}Setting up TradingAgents environment...${NC}" echo -e "${YELLOW}------------------------------------------${NC}" pushd ./third_party/TradingAgents -highlight_command "uv venv --python 3.12" -uv venv --python 3.12 +if [ ! -d ".venv" ]; then + highlight_command "uv venv --python 3.12" + uv venv --python 3.12 +else + echo -e "${YELLOW}.venv already exists, skipping venv creation.${NC}" +fi highlight_command "uv sync" uv sync popd From 2c1ebd3ef3a8dd2cc4b4b1000c77681fb5721d8f Mon Sep 17 00:00:00 2001 From: Zhaofeng Zhang <24791380+vcfgv@users.noreply.github.com> Date: Fri, 26 Sep 2025 16:20:50 +0800 Subject: [PATCH 18/18] fix: change exit code to 1 for no agent selection in main function --- python/scripts/launch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/scripts/launch.py b/python/scripts/launch.py index 6660c7fd8..6948780ff 100644 --- a/python/scripts/launch.py +++ b/python/scripts/launch.py @@ -79,7 +79,7 @@ def main(): if not selected_agents: print("No agents selected.") - exit(0) + exit(1) os.makedirs(log_dir, exist_ok=True) print(f"Logs will be saved to {log_dir}/")