Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
38f133e
feat: add diff-cover to development dependencies and update linting c…
vcfgv Sep 26, 2025
0038167
refactor: remove auto-import functionality for agents in __init__.py
vcfgv Sep 26, 2025
2fe7141
feat: update uv.lock to include aiosqlite and diff-cover dependencies
vcfgv Sep 26, 2025
c9ee9c9
feat: add error handling for agent card resolution in AgentClient
vcfgv Sep 26, 2025
b41296e
feat: implement interactive agent launcher script with logging
vcfgv Sep 26, 2025
e57bba8
feat: add interactive agent launcher and environment preparation scripts
vcfgv Sep 26, 2025
200bc36
test: add unit test for ensure_initialized card resolution failure
vcfgv Sep 26, 2025
c76ce44
fix: update environment preparation script to check for project struc…
vcfgv Sep 26, 2025
9f19205
fix: improve project root detection and error messaging in environmen…
vcfgv Sep 26, 2025
80623c0
fix: refactor environment file path handling in check_envfile_is_set …
vcfgv Sep 26, 2025
e24518f
Merge commit 'c0ca4c4e909a3d126f75570cec41f94bb9590889' into feature/…
vcfgv Sep 26, 2025
55112a2
feat: rename launch script
vcfgv Sep 26, 2025
e3bd29c
fix: update launch commands to include environment file and improve l…
vcfgv Sep 26, 2025
9295b72
refactor: update start script to improve dependency installation and …
vcfgv Sep 26, 2025
56a29fe
feat: update agent selection to include all agents and improve loggin…
vcfgv Sep 26, 2025
5369db5
fix: update frontend URL reference in log messages
vcfgv Sep 26, 2025
a3ba7f0
feat: sync Python dependencies and initialize database during compila…
vcfgv Sep 26, 2025
7dc60cc
fix: prevent redundant virtual environment creation in setup script
vcfgv Sep 26, 2025
2c1ebd3
fix: change exit code to 1 for no agent selection in main function
vcfgv Sep 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions python/scripts/launch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"""
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"
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"
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 --env-file {ENV_PATH} -m adapter --analyst {analyst}"
)
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 --env-file {ENV_PATH} -m adapter"
)
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():
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"{PROJECT_DIR}/logs/{timestamp}"

# 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 not selected_agents:
print("No agents selected.")
exit(1)

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
process = subprocess.Popen(
MAP_NAME_COMMAND[selected_agent], shell=True, stdout=logfile, stderr=logfile
)
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 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(
BACKEND_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__":
main()
87 changes: 87 additions & 0 deletions python/scripts/prepare_envs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/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 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: Failed to switch to python directory. You are in $(pwd)${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

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}"
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}"

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 ./third_party/ai-hedge-fund
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
echo -e "${GREEN}ai-hedge-fund environment setup complete.${NC}"

echo -e "${YELLOW}------------------------------------------${NC}"
echo -e "${YELLOW}Setting up TradingAgents environment...${NC}"
echo -e "${YELLOW}------------------------------------------${NC}"
pushd ./third_party/TradingAgents
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
echo -e "${GREEN}TradingAgents environment setup complete.${NC}"

echo -e "${GREEN}==========================================${NC}"
echo -e "${GREEN}All environments are set up.${NC}"
echo -e "${GREEN}==========================================${NC}"
24 changes: 23 additions & 1 deletion python/third_party/TradingAgents/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions python/third_party/ai-hedge-fund/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 0 additions & 61 deletions python/valuecell/agents/__init__.py
Original file line number Diff line number Diff line change
@@ -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
8 changes: 7 additions & 1 deletion python/valuecell/core/agent/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Loading