mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-05-01 22:43:10 +03:00
feat: add LangGraph checkpoint resume for crash recovery (#594)
Long analyses can take many minutes; a crash or interruption forced users to re-run from scratch and re-pay every LLM call. This adds an opt-in checkpoint layer backed by per-ticker SQLite databases so the graph resumes from the last successful node. How to use: - CLI: tradingagents analyze --checkpoint - CLI: tradingagents analyze --clear-checkpoints - Python: config["checkpoint_enabled"] = True Lifecycle: - propagate() recompiles the graph with a SqliteSaver when enabled and injects a deterministic thread_id derived from ticker+date so the same ticker+date resumes while a different date starts fresh. - On successful completion the per-thread checkpoint rows are cleared. - The context manager is closed in a try/finally so a crash never leaks the SQLite connection or leaves the graph in checkpoint mode. Storage: ~/.tradingagents/cache/checkpoints/<TICKER>.db (override via TRADINGAGENTS_CACHE_DIR). The checkpointer module is new (tradingagents/graph/checkpointer.py) and the GraphSetup now returns the uncompiled workflow so it can be recompiled with a saver when needed. Adds langgraph-checkpoint-sqlite>=2.0.0 dependency. 3 new tests verify the crash/resume cycle and that a different date starts fresh.
This commit is contained in:
@@ -629,7 +629,9 @@ class TestLegacyRemoval:
|
||||
create_portfolio_manager(mock_llm, memory=MagicMock())
|
||||
|
||||
def test_full_pipeline_no_regression(self, tmp_path):
|
||||
"""propagate() completes without AttributeError after legacy cleanup."""
|
||||
"""propagate() completes and stores the decision after the redesign."""
|
||||
import functools
|
||||
|
||||
fake_state = {
|
||||
"final_trade_decision": "Rating: Buy\nBuy NVDA.",
|
||||
"company_of_interest": "NVDA",
|
||||
@@ -660,6 +662,11 @@ class TestLegacyRemoval:
|
||||
mock_graph.propagator.create_initial_state.return_value = fake_state
|
||||
mock_graph.propagator.get_graph_args.return_value = {}
|
||||
mock_graph.signal_processor.process_signal.return_value = "Buy"
|
||||
# Bind the real _run_graph so propagate's call to self._run_graph executes
|
||||
# the actual write path instead of the auto-MagicMock.
|
||||
mock_graph._run_graph = functools.partial(
|
||||
TradingAgentsGraph._run_graph, mock_graph
|
||||
)
|
||||
TradingAgentsGraph.propagate(mock_graph, "NVDA", "2026-01-10")
|
||||
entries = mock_graph.memory_log.load_entries()
|
||||
assert len(entries) == 1
|
||||
|
||||
Reference in New Issue
Block a user