chore: remove code nothing uses

- is_yahoo_safe, has_checkpoint, get_wall_times, the graph's curr_state and the project_dir config key
- the CLI's final_report and current_agent state, its duplicate get_analysis_date and the save_report_to_disk wrapper
- the dotenv import guard (a hard dependency) and a warning filter for langgraph-checkpoint 4.0.3
This commit is contained in:
Yijia-Xiao
2026-09-24 04:31:05 +00:00
parent 41fc25ac0d
commit 9b14233a24
11 changed files with 22 additions and 175 deletions
+1 -55
View File
@@ -112,9 +112,7 @@ class MessageBuffer:
self.messages = deque(maxlen=max_length)
self.tool_calls = deque(maxlen=max_length)
self.current_report = None
self.final_report = None # Store the complete final report
self.agent_status = {}
self.current_agent = None
self.report_sections = {}
self.selected_analysts = []
self._processed_message_ids = set()
@@ -148,8 +146,6 @@ class MessageBuffer:
# Reset other state
self.current_report = None
self.final_report = None
self.current_agent = None
self.messages.clear()
self.tool_calls.clear()
self._processed_message_ids.clear()
@@ -186,7 +182,6 @@ class MessageBuffer:
def update_agent_status(self, agent, status):
if agent in self.agent_status:
self.agent_status[agent] = status
self.current_agent = agent
def update_report_section(self, section_name, content):
if section_name in self.report_sections:
@@ -219,50 +214,6 @@ class MessageBuffer:
f"### {section_titles[latest_section]}\n{latest_content}"
)
# Update the final complete report
self._update_final_report()
def _update_final_report(self):
report_parts = []
# Analyst Team Reports - use .get() to handle missing sections
analyst_sections = ["market_report", "sentiment_report", "news_report", "fundamentals_report"]
if any(self.report_sections.get(section) for section in analyst_sections):
report_parts.append("## Analyst Team Reports")
if self.report_sections.get("market_report"):
report_parts.append(
f"### Market Analysis\n{self.report_sections['market_report']}"
)
if self.report_sections.get("sentiment_report"):
report_parts.append(
f"### Social Sentiment\n{self.report_sections['sentiment_report']}"
)
if self.report_sections.get("news_report"):
report_parts.append(
f"### News Analysis\n{self.report_sections['news_report']}"
)
if self.report_sections.get("fundamentals_report"):
report_parts.append(
f"### Fundamentals Analysis\n{self.report_sections['fundamentals_report']}"
)
# Research Team Reports
if self.report_sections.get("investment_plan"):
report_parts.append("## Research Team Decision")
report_parts.append(f"{self.report_sections['investment_plan']}")
# Trading Team Reports
if self.report_sections.get("trader_investment_plan"):
report_parts.append("## Trading Team Plan")
report_parts.append(f"{self.report_sections['trader_investment_plan']}")
# Portfolio Management Decision
if self.report_sections.get("final_trade_decision"):
report_parts.append("## Portfolio Management Decision")
report_parts.append(f"{self.report_sections['final_trade_decision']}")
self.final_report = "\n\n".join(report_parts) if report_parts else None
message_buffer = MessageBuffer()
@@ -780,11 +731,6 @@ def get_analysis_date():
)
def save_report_to_disk(final_state, ticker: str, save_path: Path):
"""Save the complete analysis report to disk (shared CLI/API writer)."""
return write_report_tree(final_state, ticker, save_path)
def display_complete_report(final_state):
"""Display the complete analysis report sequentially (avoids truncation)."""
console.print()
@@ -1344,7 +1290,7 @@ def run_analysis(checkpoint: bool | None = None, portfolio=None):
).strip()
save_path = Path(save_path_str)
try:
report_file = save_report_to_disk(final_state, selections["ticker"], save_path)
report_file = write_report_tree(final_state, selections["ticker"], save_path)
console.print(f"\n[green]✓ Report saved to:[/green] {save_path.resolve()}")
console.print(f" [dim]Complete report:[/dim] {report_file.name}")
except Exception as e:
-33
View File
@@ -99,39 +99,6 @@ def filter_analysts_for_asset_type(
]
def get_analysis_date() -> str:
"""Prompt the user to enter a date in YYYY-MM-DD format."""
import re
from datetime import datetime
def validate_date(date_str: str) -> bool:
if not re.match(r"^\d{4}-\d{2}-\d{2}$", date_str):
return False
try:
datetime.strptime(date_str, "%Y-%m-%d")
return True
except ValueError:
return False
date = questionary.text(
"Enter the analysis date (YYYY-MM-DD):",
validate=lambda x: validate_date(x.strip())
or "Please enter a valid date in YYYY-MM-DD format.",
style=questionary.Style(
[
("text", "fg:green"),
("highlighted", "noinherit"),
]
),
).ask()
if not date:
console.print("\n[red]No date provided. Exiting...[/red]")
exit(1)
return date.strip()
def _matching_choice(options, default):
"""The option value equal to ``default``, or None to leave the menu as is."""
return next((value for _, value in options if value == default), None)
+4 -7
View File
@@ -49,7 +49,7 @@ class AnalystWallTimeTrackerTests(unittest.TestCase):
tracker.mark_started("market", started_at=10.0)
tracker.mark_completed("market", completed_at=13.5)
self.assertEqual(tracker.get_wall_times(), {"market": 3.5})
self.assertEqual(tracker.format_summary(), "Analyst wall time: Market 3.50s")
def test_formats_summary_in_plan_order(self):
plan = build_analyst_execution_plan(["news", "market"])
@@ -70,21 +70,18 @@ class AnalystWallTimeTrackerTests(unittest.TestCase):
tracker = AnalystWallTimeTracker(plan)
sync_analyst_tracker_from_chunk(tracker, {}, now=10.0)
self.assertEqual(tracker.get_wall_times(), {})
self.assertEqual(tracker.format_summary(), "Analyst wall time: pending")
sync_analyst_tracker_from_chunk(
tracker,
{"market_report": "done"},
now=13.0,
)
self.assertEqual(tracker.get_wall_times(), {"market": 3.0})
self.assertEqual(tracker.format_summary(), "Analyst wall time: Market 3.00s")
sync_analyst_tracker_from_chunk(
tracker,
{"market_report": "done", "news_report": "done"},
now=18.0,
)
self.assertEqual(
tracker.get_wall_times(),
{"market": 3.0, "news": 5.0},
)
self.assertEqual(tracker.format_summary(), "Analyst wall time: Market 3.00s | News 5.00s")
+9 -10
View File
@@ -10,7 +10,6 @@ from tradingagents.graph.checkpointer import (
checkpoint_step,
clear_checkpoint,
get_checkpointer,
has_checkpoint,
thread_id,
)
@@ -63,7 +62,7 @@ class TestCheckpointResume(unittest.TestCase):
graph.invoke({"count": 0}, config=cfg)
# Checkpoint should exist at step 1 (analyst completed)
self.assertTrue(has_checkpoint(self.tmpdir, self.ticker, self.date))
self.assertIsNotNone(checkpoint_step(self.tmpdir, self.ticker, self.date))
step = checkpoint_step(self.tmpdir, self.ticker, self.date)
self.assertEqual(step, 1)
@@ -90,11 +89,11 @@ class TestCheckpointResume(unittest.TestCase):
with self.assertRaises(RuntimeError):
graph.invoke({"count": 0}, config=cfg)
self.assertTrue(has_checkpoint(self.tmpdir, self.ticker, self.date))
self.assertIsNotNone(checkpoint_step(self.tmpdir, self.ticker, self.date))
# Clear it
clear_checkpoint(self.tmpdir, self.ticker, self.date)
self.assertFalse(has_checkpoint(self.tmpdir, self.ticker, self.date))
self.assertIsNone(checkpoint_step(self.tmpdir, self.ticker, self.date))
# Fresh run succeeds from scratch
_should_crash = False
@@ -119,10 +118,10 @@ class TestCheckpointResume(unittest.TestCase):
with self.assertRaises(RuntimeError):
graph.invoke({"count": 0}, config={"configurable": {"thread_id": tid1}})
self.assertTrue(has_checkpoint(self.tmpdir, self.ticker, self.date))
self.assertIsNotNone(checkpoint_step(self.tmpdir, self.ticker, self.date))
# date2 should have no checkpoint
self.assertFalse(has_checkpoint(self.tmpdir, self.ticker, date2))
self.assertIsNone(checkpoint_step(self.tmpdir, self.ticker, date2))
# Run with date2 — should start fresh and succeed
_should_crash = False
@@ -137,7 +136,7 @@ class TestCheckpointResume(unittest.TestCase):
self.assertEqual(result["count"], 11)
# Original date checkpoint still exists (untouched)
self.assertTrue(has_checkpoint(self.tmpdir, self.ticker, self.date))
self.assertIsNotNone(checkpoint_step(self.tmpdir, self.ticker, self.date))
class TestCheckpointSignature(unittest.TestCase):
@@ -178,9 +177,9 @@ class TestCheckpointSignature(unittest.TestCase):
with self.assertRaises(RuntimeError):
graph.invoke({"count": 0}, config={"configurable": {"thread_id": tid1}})
self.assertTrue(has_checkpoint(self.tmpdir, self.ticker, self.date, sig1))
self.assertIsNotNone(checkpoint_step(self.tmpdir, self.ticker, self.date, sig1))
# A different graph shape has no checkpoint to resume from.
self.assertFalse(has_checkpoint(self.tmpdir, self.ticker, self.date, sig2))
self.assertIsNone(checkpoint_step(self.tmpdir, self.ticker, self.date, sig2))
_should_crash = False
tid2 = thread_id(self.ticker, self.date, sig2)
@@ -190,7 +189,7 @@ class TestCheckpointSignature(unittest.TestCase):
result = graph.invoke({"count": 0}, config={"configurable": {"thread_id": tid2}})
self.assertEqual(result["count"], 11)
# sig1's checkpoint remains untouched.
self.assertTrue(has_checkpoint(self.tmpdir, self.ticker, self.date, sig1))
self.assertIsNotNone(checkpoint_step(self.tmpdir, self.ticker, self.date, sig1))
def test_run_signature_captures_graph_shape(self):
from tradingagents.graph.trading_graph import TradingAgentsGraph
-12
View File
@@ -7,7 +7,6 @@ import pytest
from tradingagents.dataflows.symbol_utils import (
NoMarketDataError,
crypto_base,
is_yahoo_safe,
normalize_symbol,
)
@@ -88,17 +87,6 @@ class TestNoMarketDataError(unittest.TestCase):
self.assertEqual(err.canonical, "FOOBAR")
@pytest.mark.unit
class TestIsYahooSafe(unittest.TestCase):
def test_accepts_structural_chars(self):
for sym in ("AAPL", "GC=F", "^GSPC", "BRK.B", "BTC-USD"):
self.assertTrue(is_yahoo_safe(sym))
def test_rejects_slash_and_space(self):
for sym in ("a/b", "AA PL", ""):
self.assertFalse(is_yahoo_safe(sym))
@pytest.mark.unit
class TestCryptoBase(unittest.TestCase):
def test_resolves_known_crypto_forms(self):
+8 -36
View File
@@ -1,37 +1,9 @@
import contextlib
import warnings
from dotenv import find_dotenv, load_dotenv
# Load .env files at package import so DEFAULT_CONFIG's env-var overlay
# (and every llm_clients consumer) sees the user's keys regardless of
# which entry point started the process. find_dotenv(usecwd=True) walks
# from the CWD, so the installed `tradingagents` console script picks up
# the project's .env instead of stepping up from site-packages.
# load_dotenv defaults to override=False, so it never clobbers values
# the caller has already exported.
try:
from dotenv import find_dotenv, load_dotenv
load_dotenv(find_dotenv(usecwd=True))
load_dotenv(find_dotenv(".env.enterprise", usecwd=True), override=False)
except ImportError:
pass
# langchain-core 1.3.3 calls surface_langchain_deprecation_warnings() in
# its own __init__, which prepends default-action filters for its
# subclassed warning categories. To suppress a specific warning we must
# install our filter AFTER langchain-core has installed its own, so import
# it first. The package is a guaranteed transitive dep via langgraph.
with contextlib.suppress(ImportError):
import langchain_core # noqa: F401
# langgraph-checkpoint 4.0.3 calls Reviver() at module load without an
# explicit allowed_objects, which triggers a noisy pending-deprecation
# warning from langchain-core 1.3.3 on every interpreter start. The fix
# is already merged upstream (langchain-ai/langgraph#7743, 2026-05-08)
# and will arrive in the next langgraph-checkpoint release. Remove this
# block (and the langchain_core preload above) when we bump past it.
warnings.filterwarnings(
"ignore",
message=r"The default value of `allowed_objects`.*",
category=PendingDeprecationWarning,
)
# Load .env at package import so DEFAULT_CONFIG's env-var overlay and every LLM
# client see the user's keys whichever entry point started the process.
# usecwd=True walks from the working directory, so the installed console script
# finds the project's .env rather than looking beside site-packages. Values the
# caller has already exported are never overridden.
load_dotenv(find_dotenv(usecwd=True))
load_dotenv(find_dotenv(".env.enterprise", usecwd=True), override=False)
-7
View File
@@ -71,9 +71,6 @@ _ALIASES = {
"FRA40": "^FCHI", "EU50": "^STOXX50E", "HK50": "^HSI",
}
# Yahoo symbols may contain letters, digits, and these structural characters.
_YAHOO_SAFE = re.compile(r"^[A-Za-z0-9._\-\^=]+$")
# HKEX codes as Yahoo spells them: the number zero-padded to 4 digits (#957).
_HK_CODE = re.compile(r"^(\d{1,5})\.HK$")
_SHANGHAI_SH = re.compile(r"^(\d{6})\.SH$")
@@ -150,7 +147,3 @@ def normalize_symbol(raw: str) -> str:
logger.info("Resolved symbol %r to Yahoo symbol %r", raw, canonical)
return canonical
def is_yahoo_safe(symbol: str) -> bool:
"""True when ``symbol`` only contains characters Yahoo symbols use."""
return bool(symbol) and _YAHOO_SAFE.fullmatch(symbol) is not None
-1
View File
@@ -70,7 +70,6 @@ def _apply_env_overrides(config: dict) -> dict:
DEFAULT_CONFIG = _apply_env_overrides({
"project_dir": os.path.abspath(os.path.join(os.path.dirname(__file__), ".")),
"results_dir": os.getenv("TRADINGAGENTS_RESULTS_DIR") or os.path.join(_TRADINGAGENTS_HOME, "logs"),
"data_cache_dir": os.getenv("TRADINGAGENTS_CACHE_DIR") or os.path.join(_TRADINGAGENTS_HOME, "cache"),
"memory_log_path": os.getenv("TRADINGAGENTS_MEMORY_LOG_PATH") or os.path.join(_TRADINGAGENTS_HOME, "memory", "trading_memory.md"),
-3
View File
@@ -103,9 +103,6 @@ class AnalystWallTimeTracker:
finished_at = monotonic() if completed_at is None else completed_at
self._wall_times[analyst_key] = max(0.0, finished_at - started_at)
def get_wall_times(self) -> dict[str, float]:
return dict(self._wall_times)
def format_summary(self) -> str:
parts = []
for spec in self.plan.specs:
-5
View File
@@ -51,11 +51,6 @@ def get_checkpointer(data_dir: str | Path, ticker: str) -> Generator[SqliteSaver
conn.close()
def has_checkpoint(data_dir: str | Path, ticker: str, date: str, signature: str = "") -> bool:
"""Check whether a resumable checkpoint exists for ticker+date."""
return checkpoint_step(data_dir, ticker, date, signature) is not None
def checkpoint_step(data_dir: str | Path, ticker: str, date: str, signature: str = "") -> int | None:
"""Return the step number of the latest checkpoint, or None if none exists."""
db = _db_path(data_dir, ticker)
-6
View File
@@ -145,9 +145,6 @@ class TradingAgentsGraph:
)
self.reflector = Reflector(self.quick_thinking_llm)
# State tracking
self.curr_state = None
# Graph-shape-affecting run choices, kept for the checkpoint signature.
self.selected_analysts = tuple(selected_analysts)
@@ -538,9 +535,6 @@ class TradingAgentsGraph:
else:
final_state = self.graph.invoke(graph_input, **args)
# Store current state for reflection.
self.curr_state = final_state
# Log state to disk.
self._log_state(trade_date, final_state)