Skip to content
58 changes: 0 additions & 58 deletions python/configs/agent_cards/sec_agent.json

This file was deleted.

21 changes: 10 additions & 11 deletions python/scripts/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
from pathlib import Path
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",
Expand All @@ -30,9 +28,9 @@
"ValuationAnalystAgent": "valuation_analyst",
"WarrenBuffettAgent": "warren_buffett",
}
SEC_AGENT_NAME = "SECAgent"
TRADING_AGENTS_NAME = "TradingAgents"
AGENTS = list(MAP_NAME_ANALYST.keys()) + [SEC_AGENT_NAME, TRADING_AGENTS_NAME]
RESEARCH_AGENT_NAME = "ResearchAgent"
AGENTS = list(MAP_NAME_ANALYST.keys()) + [TRADING_AGENTS_NAME, RESEARCH_AGENT_NAME]

PROJECT_DIR = Path(__file__).resolve().parent.parent.parent
PYTHON_DIR = PROJECT_DIR / "python"
Expand All @@ -50,12 +48,12 @@
MAP_NAME_COMMAND[name] = (
f"cd {PYTHON_DIR_STR}/third_party/ai-hedge-fund && uv run --env-file {ENV_PATH_STR} -m adapter --analyst {analyst}"
)
MAP_NAME_COMMAND[SEC_AGENT_NAME] = (
f"uv run --env-file {ENV_PATH_STR} -m valuecell.agents.sec_agent"
)
MAP_NAME_COMMAND[TRADING_AGENTS_NAME] = (
f"cd {PYTHON_DIR_STR}/third_party/TradingAgents && uv run --env-file {ENV_PATH_STR} -m adapter"
)
MAP_NAME_COMMAND[RESEARCH_AGENT_NAME] = (
f"uv run --env-file {ENV_PATH_STR} -m valuecell.agents.research_agent"
)
BACKEND_COMMAND = (
f"cd {PYTHON_DIR_STR} && uv run --env-file {ENV_PATH_STR} -m valuecell.server.main"
)
Expand All @@ -77,10 +75,11 @@ def main():
log_dir = f"{PROJECT_DIR_STR}/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()
# selected_agents = questionary.checkbox(
# "Choose agents to launch (use space to select, enter to confirm):",
# choices=AGENTS,
# ).ask()
selected_agents = AGENTS

if not selected_agents:
print("No agents selected.")
Expand Down
11 changes: 7 additions & 4 deletions python/valuecell/agents/research_agent/core.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os
from typing import AsyncGenerator, Dict, Iterator, Optional
from typing import AsyncGenerator, Dict, Optional

from agno.agent import Agent, RunOutputEvent
from agno.agent import Agent
from agno.db.in_memory import InMemoryDb
from agno.models.google import Gemini
from agno.models.openrouter import OpenRouter
Expand All @@ -17,12 +17,13 @@
fetch_event_sec_filings,
fetch_periodic_sec_filings,
)
from valuecell.agents.utils.context import build_ctx_from_dep
from valuecell.core.agent.responses import streaming
from valuecell.core.types import BaseAgent, StreamResponse
from valuecell.utils.env import agent_debug_mode_enabled


def _get_model_based_on_env() -> str:
def _get_model_based_on_env():
model_id = os.getenv("RESEARCH_AGENT_MODEL_ID")
if os.getenv("GOOGLE_API_KEY"):
return Gemini(id=model_id or "gemini-2.5-flash")
Expand Down Expand Up @@ -58,11 +59,13 @@ async def stream(
task_id: str,
dependencies: Optional[Dict] = None,
) -> AsyncGenerator[StreamResponse, None]:
response_stream: Iterator[RunOutputEvent] = self.knowledge_research_agent.arun(
response_stream = self.knowledge_research_agent.arun(
query,
stream=True,
stream_intermediate_steps=True,
session_id=conversation_id,
add_dependencies_to_context=True,
dependencies=build_ctx_from_dep(dependencies),
)
async for event in response_stream:
if event.event == "RunContent":
Expand Down
2 changes: 1 addition & 1 deletion python/valuecell/agents/research_agent/prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
You are a financial research assistant. Your primary objective is to satisfy the user's information request about a company's financials, filings, or performance with accurate, sourceable, and actionable answers.
</purpose>

-<tools>
<tools>
- fetch_periodic_sec_filings(ticker_or_cik, forms, year?, quarter?, limit?): Use this for scheduled reports like 10-K/10-Q when you need primary-source facts (revenue, net income, MD&A text). Prefer batching by year to reduce calls. Note: year/quarter filters apply to filing_date (edgar behavior), not period_of_report. If year is omitted, the tool returns the latest filings using `limit` (default 10). If quarter is provided, year must also be provided.
- fetch_event_sec_filings(ticker_or_cik, forms, start_date?, end_date?, limit?): Use this for event-driven filings like 8-K and ownership forms (3/4/5). Use date ranges and limits to control scope.
- Knowledge base search: Use the agent's internal knowledge index to find summaries, historical context, analyst commentary, and previously ingested documents.
Expand Down
Empty file.
49 changes: 49 additions & 0 deletions python/valuecell/agents/utils/context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from typing import Any, Dict, Optional

from valuecell.core.constants import LANGUAGE, TIMEZONE


def build_ctx_from_dep(
dep: Optional[Dict[str, Any]],
) -> Dict[str, str] | None:
if not dep:
return None

context = {}
lang_ctx = _build_lang_ctx_from_dep(dep)
if lang_ctx:
context["compose_answer_hint"] = lang_ctx

return context


def _build_lang_ctx_from_dep(
dependencies: Optional[Dict[str, Any]],
) -> str | None:
if not dependencies:
return None

user_lang = dependencies.get(LANGUAGE)
user_tz = dependencies.get(TIMEZONE)

parts = []
parts.append(
"When composing your answer, consider the user's language and timezone:"
)
if user_lang:
parts.append(f"- Preferred language: {user_lang}")
else:
parts.append(
"- Preferred language: not set. Infer the user's language from their query and respond in that language."
)

if user_tz:
parts.append(
f"- Timezone: {user_tz} (use this to interpret or present times/dates)"
)
else:
parts.append(
"- Timezone: not set. Do NOT ask the user for their timezone unless absolutely necessary. Instead, infer timezone from context (locale, timestamps, phrasing) when possible; if you cannot reasonably infer it, default to UTC when presenting absolute times."
)

return "\n".join(parts)
2 changes: 1 addition & 1 deletion python/valuecell/core/agent/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ async def _setup_client(self):
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`."
" Check the agent logs for more details."
) from e
self._client = client_factory.create(self.agent_card)

Expand Down
2 changes: 1 addition & 1 deletion python/valuecell/core/agent/tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ async def test_ensure_initialized_card_resolution_failure(self):

error_message = str(exc_info.value)
assert "Failed to resolve agent card" in error_message
assert "scripts/launch_agent.py" in error_message
assert "Check the agent logs" in error_message
assert "Connection timeout" in str(
exc_info.value.__cause__
) # Original exception should be chained