Merge pull request #86 from aguzererler/copilot/review-comments

fix: four follow-up issues from PR #85 dataflows hardening
This commit is contained in:
ahmet guzererler 2026-03-22 10:05:09 +01:00 committed by GitHub
commit b2fe6ec8c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 20 additions and 19 deletions

View File

@ -27,6 +27,7 @@ import time
from rich import box from rich import box
from rich.align import Align from rich.align import Align
from rich.rule import Rule from rich.rule import Rule
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeElapsedColumn
from tradingagents.graph.trading_graph import TradingAgentsGraph from tradingagents.graph.trading_graph import TradingAgentsGraph
from tradingagents.report_paths import get_daily_dir, get_market_dir, get_ticker_dir from tradingagents.report_paths import get_daily_dir, get_market_dir, get_ticker_dir
@ -1587,10 +1588,8 @@ def run_pipeline(
f" [dim]{c.sector} · {c.conviction.upper()} conviction[/dim]" f" [dim]{c.sector} · {c.conviction.upper()} conviction[/dim]"
) )
console.print() console.print()
import time as _time
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeElapsedColumn
pipeline_start = _time.monotonic() pipeline_start = time.monotonic()
with Progress( with Progress(
SpinnerColumn(), SpinnerColumn(),
@ -1604,17 +1603,17 @@ def run_pipeline(
overall = progress.add_task("[bold]Pipeline progress[/bold]", total=len(candidates)) overall = progress.add_task("[bold]Pipeline progress[/bold]", total=len(candidates))
def on_done(result, done_count, total_count): def on_done(result, done_count, total_count):
ticker_elapsed = _time.monotonic() - pipeline_start ticker_elapsed = result.elapsed_seconds
if result.error: if result.error:
console.print( console.print(
f" [red]✗ {result.ticker}[/red]" f" [red]✗ {result.ticker}[/red]"
f" [dim]failed ({ticker_elapsed:.0f}s elapsed) — {result.error[:80]}[/dim]" f" [dim]failed ({ticker_elapsed:.0f}s) — {result.error[:80]}[/dim]"
) )
else: else:
decision_preview = str(result.final_trade_decision)[:70].replace("\n", " ") decision_preview = str(result.final_trade_decision)[:70].replace("\n", " ")
console.print( console.print(
f" [green]✓ {result.ticker}[/green]" f" [green]✓ {result.ticker}[/green]"
f" [dim]({done_count}/{total_count}, {ticker_elapsed:.0f}s elapsed)[/dim]" f" [dim]({done_count}/{total_count}, {ticker_elapsed:.0f}s)[/dim]"
f"{decision_preview}" f"{decision_preview}"
) )
progress.advance(overall) progress.advance(overall)
@ -1630,7 +1629,7 @@ def run_pipeline(
console.print(f"[red]Pipeline failed: {e}[/red]") console.print(f"[red]Pipeline failed: {e}[/red]")
raise typer.Exit(1) raise typer.Exit(1)
elapsed_total = _time.monotonic() - pipeline_start elapsed_total = time.monotonic() - pipeline_start
console.print( console.print(
f"\n[bold green]All {len(candidates)} ticker(s) finished in {elapsed_total:.0f}s[/bold green]\n" f"\n[bold green]All {len(candidates)} ticker(s) finished in {elapsed_total:.0f}s[/bold green]\n"
) )

View File

@ -1,8 +1,9 @@
import threading import threading
import pytest import pytest
from cli.stats_handler import StatsCallbackHandler from cli.stats_handler import StatsCallbackHandler
from langchain_core.outputs import LLMResult, Generation from langchain_core.outputs import LLMResult, Generation, ChatGeneration
from langchain_core.messages import AIMessage from langchain_core.messages import AIMessage
from langchain_core.messages.ai import UsageMetadata
def test_stats_handler_initial_state(): def test_stats_handler_initial_state():
handler = StatsCallbackHandler() handler = StatsCallbackHandler()
@ -35,11 +36,10 @@ def test_stats_handler_on_tool_start():
def test_stats_handler_on_llm_end_with_usage(): def test_stats_handler_on_llm_end_with_usage():
handler = StatsCallbackHandler() handler = StatsCallbackHandler()
# Mock usage metadata # ChatGeneration wraps chat messages; Generation (plain text) has no .message attr.
usage_metadata = {"input_tokens": 10, "output_tokens": 20} usage_metadata = UsageMetadata(input_tokens=10, output_tokens=20, total_tokens=30)
message = AIMessage(content="test response") message = AIMessage(content="test response", usage_metadata=usage_metadata)
message.usage_metadata = usage_metadata generation = ChatGeneration(message=message)
generation = Generation(message=message, text="test response")
response = LLMResult(generations=[[generation]]) response = LLMResult(generations=[[generation]])
handler.on_llm_end(response) handler.on_llm_end(response)
@ -83,11 +83,10 @@ def test_stats_handler_thread_safety():
handler.on_llm_start({}, []) handler.on_llm_start({}, [])
handler.on_tool_start({}, "") handler.on_tool_start({}, "")
# Mock usage metadata for on_llm_end # ChatGeneration wraps chat messages with usage_metadata
usage_metadata = {"input_tokens": 1, "output_tokens": 1} usage_metadata = UsageMetadata(input_tokens=1, output_tokens=1, total_tokens=2)
message = AIMessage(content="x") message = AIMessage(content="x", usage_metadata=usage_metadata)
message.usage_metadata = usage_metadata generation = ChatGeneration(message=message)
generation = Generation(message=message, text="x")
response = LLMResult(generations=[[generation]]) response = LLMResult(generations=[[generation]])
handler.on_llm_end(response) handler.on_llm_end(response)

View File

@ -168,7 +168,7 @@ def get_stock_stats_indicators_window(
ind_string += f"{date_str}: {value}\n" ind_string += f"{date_str}: {value}\n"
except Exception as e: except Exception as e:
print(f"Error getting bulk stockstats data: {e}") logger.warning("Bulk stockstats failed for %s/%s, falling back to per-day loop: %s", symbol, indicator, e)
# Fallback to original implementation if bulk method fails # Fallback to original implementation if bulk method fails
ind_string = "" ind_string = ""
curr_date_dt = datetime.strptime(curr_date, "%Y-%m-%d") curr_date_dt = datetime.strptime(curr_date, "%Y-%m-%d")

View File

@ -70,6 +70,7 @@ class TickerResult:
final_trade_decision: str = "" final_trade_decision: str = ""
error: str | None = None error: str | None = None
elapsed_seconds: float = 0.0
# ─── Parsing ────────────────────────────────────────────────────────────────── # ─── Parsing ──────────────────────────────────────────────────────────────────
@ -207,6 +208,7 @@ def run_ticker_analysis(
result.final_trade_decision = decision result.final_trade_decision = decision
elapsed = time.monotonic() - t0 elapsed = time.monotonic() - t0
result.elapsed_seconds = elapsed
logger.info( logger.info(
"[%s] ✓ Analysis complete in %.0fs — decision: %s", "[%s] ✓ Analysis complete in %.0fs — decision: %s",
candidate.ticker, elapsed, str(decision)[:80], candidate.ticker, elapsed, str(decision)[:80],
@ -214,6 +216,7 @@ def run_ticker_analysis(
except Exception as exc: except Exception as exc:
elapsed = time.monotonic() - t0 elapsed = time.monotonic() - t0
result.elapsed_seconds = elapsed
logger.error( logger.error(
"[%s] ✗ Analysis FAILED after %.0fs: %s", "[%s] ✗ Analysis FAILED after %.0fs: %s",
candidate.ticker, elapsed, exc, exc_info=True, candidate.ticker, elapsed, exc, exc_info=True,