diff --git a/cli/run.py b/cli/run.py index bbcd1e0d4..b8a1792f0 100644 --- a/cli/run.py +++ b/cli/run.py @@ -1,6 +1,5 @@ """Running one analysis from the CLI: build the graph, stream it into the live view, save the report.""" -import datetime import os import sys import time @@ -31,7 +30,6 @@ from tradingagents.graph.analyst_execution import ( build_analyst_execution_plan, ) from tradingagents.graph.trading_graph import TradingAgentsGraph -from tradingagents.reporting import write_report_tree def _run_directory(config: dict, ticker: str, trade_date: str) -> Path: @@ -384,29 +382,26 @@ def run_analysis(checkpoint: bool | None = None, portfolio=None, flags=None): ) console.print(f"[dim]{analyst_wall_time_tracker.format_summary()}[/dim]") - _offer_reports(final_state, graph, config, selections["ticker"], + _offer_reports(final_state, graph, selections["ticker"], save=flags.get("save"), show=flags.get("show")) -def _offer_reports(final_state, graph, config, ticker, save=None, show=None): +def _offer_reports(final_state, graph, ticker, save=None, show=None): """Save the report tree and show it; ``save``/``show`` answer the questions when given.""" asked = save is None if asked: save = typer.prompt("Save report?", default="Y").strip().upper() in ("Y", "YES", "") if save: - timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") # Under results_dir, not the working directory: in Docker the working # directory is inside the container and the report goes with it, while # results_dir is the mounted volume the rest of the run already writes to. - save_path = (Path(config["results_dir"]) / "reports" - / f"{safe_ticker_component(ticker)}_{timestamp}") + save_path = graph.default_report_path(ticker) if asked: # someone at the prompt may pick another folder save_path = Path(typer.prompt( "Save path (press Enter for default)", default=str(save_path) ).strip()) try: - report_file = write_report_tree(final_state, ticker, save_path, - settings=graph.run_settings()) + report_file = graph.save_reports(final_state, 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: diff --git a/tests/test_cli_headless.py b/tests/test_cli_headless.py index 89972eab8..3ba31b20f 100644 --- a/tests/test_cli_headless.py +++ b/tests/test_cli_headless.py @@ -95,10 +95,14 @@ def test_save_and_show_answer_the_questions_after_the_run(monkeypatch, tmp_path) monkeypatch.setattr(run.typer, "prompt", no_prompt) shown = [] monkeypatch.setattr(run, "display_complete_report", lambda state: shown.append(state)) - graph = type("G", (), {"run_settings": lambda self: {}})() + from tradingagents.graph.trading_graph import TradingAgentsGraph + + graph = object.__new__(TradingAgentsGraph) + graph.config = {"results_dir": str(tmp_path)} + graph.run_settings = lambda: {} state = {"market_report": "M", "final_trade_decision": "**Rating**: Hold"} - run._offer_reports(state, graph, {"results_dir": str(tmp_path)}, "NVDA", save=True, show=False) + run._offer_reports(state, graph, "NVDA", save=True, show=False) assert list(tmp_path.glob("reports/NVDA_*/complete_report.md")) assert shown == [] diff --git a/tests/test_reporting.py b/tests/test_reporting.py index 3ddd2b3fc..1f634a89c 100644 --- a/tests/test_reporting.py +++ b/tests/test_reporting.py @@ -50,8 +50,10 @@ def test_save_reports_explicit_path(tmp_path): @pytest.mark.unit def test_save_reports_defaults_under_results_dir(tmp_path): - mock_self = SimpleNamespace(config={"results_dir": str(tmp_path)}, run_settings=lambda: SETTINGS) - out = TradingAgentsGraph.save_reports(mock_self, _state(), "AAPL") + graph = object.__new__(TradingAgentsGraph) + graph.config = {"results_dir": str(tmp_path)} + graph.run_settings = lambda: SETTINGS + out = graph.save_reports(_state(), "AAPL") assert out.exists() assert out.parent.parent.name == "reports" # results_dir/reports/AAPL_/... assert out.parent.name.startswith("AAPL_") diff --git a/tradingagents/graph/trading_graph.py b/tradingagents/graph/trading_graph.py index d2e29bdb6..180ab65b5 100644 --- a/tradingagents/graph/trading_graph.py +++ b/tradingagents/graph/trading_graph.py @@ -294,14 +294,14 @@ class TradingAgentsGraph: an explicit ``save_path`` or let it default under ``results_dir``. """ if save_path is None: - stamp = datetime.now().strftime("%Y%m%d_%H%M%S") - save_path = ( - Path(self.config["results_dir"]) - / "reports" - / f"{safe_ticker_component(ticker)}_{stamp}" - ) + save_path = self.default_report_path(ticker) return write_report_tree(final_state, ticker, save_path, settings=self.run_settings()) + def default_report_path(self, ticker) -> Path: + """Where a run's reports go unless told otherwise: under results_dir, stamped now.""" + stamp = datetime.now().strftime("%Y%m%d_%H%M%S") + return Path(self.config["results_dir"]) / "reports" / f"{safe_ticker_component(ticker)}_{stamp}" + def create_run_state(self, company_name, trade_date, asset_type: str = "stock", portfolio=None): """Build a run's initial state; propagate() and the CLI both start here.