From c3bce5f360ae0f9d383601f31fbc3f864fe0b49f Mon Sep 17 00:00:00 2001 From: Alex Schneider Date: Wed, 25 Mar 2026 20:28:29 +0100 Subject: [PATCH] Gate Adanos social sentiment behind explicit config --- tests/test_social_sentiment_tools.py | 18 ++++++++++------ .../agents/analysts/social_media_analyst.py | 21 +++++++++++++------ tradingagents/dataflows/interface.py | 15 +++++++++++++ tradingagents/default_config.py | 2 +- tradingagents/graph/trading_graph.py | 13 ++++++------ 5 files changed, 49 insertions(+), 20 deletions(-) diff --git a/tests/test_social_sentiment_tools.py b/tests/test_social_sentiment_tools.py index 22d96dae..aa56ff8a 100644 --- a/tests/test_social_sentiment_tools.py +++ b/tests/test_social_sentiment_tools.py @@ -7,15 +7,21 @@ from tradingagents.dataflows import adanos_social, interface class SocialSentimentToolTests(unittest.TestCase): def test_route_to_vendor_supports_social_data(self): - with patch.dict( - interface.VENDOR_METHODS["get_social_sentiment"], - {"adanos": lambda ticker, curr_date, look_back_days: f"{ticker}|{curr_date}|{look_back_days}"}, - clear=True, - ): - result = interface.route_to_vendor("get_social_sentiment", "NVDA", "2026-01-15", 5) + with patch("tradingagents.dataflows.interface.get_vendor", return_value="adanos"): + with patch.dict( + interface.VENDOR_METHODS["get_social_sentiment"], + {"adanos": lambda ticker, curr_date, look_back_days: f"{ticker}|{curr_date}|{look_back_days}"}, + clear=True, + ): + result = interface.route_to_vendor("get_social_sentiment", "NVDA", "2026-01-15", 5) self.assertEqual(result, "NVDA|2026-01-15|5") + def test_route_to_vendor_requires_explicit_social_vendor(self): + with patch("tradingagents.dataflows.interface.get_vendor", return_value="default"): + with self.assertRaises(RuntimeError): + interface.route_to_vendor("get_social_sentiment", "NVDA", "2026-01-15", 5) + def test_social_tool_routes_to_vendor(self): with patch("tradingagents.agents.utils.social_data_tools.route_to_vendor", return_value="ok") as mock_route: result = social_tool.invoke( diff --git a/tradingagents/agents/analysts/social_media_analyst.py b/tradingagents/agents/analysts/social_media_analyst.py index f3e6dc9b..0e91a519 100644 --- a/tradingagents/agents/analysts/social_media_analyst.py +++ b/tradingagents/agents/analysts/social_media_analyst.py @@ -2,7 +2,7 @@ from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder import time import json from tradingagents.agents.utils.agent_utils import build_instrument_context, get_news, get_social_sentiment -from tradingagents.dataflows.config import get_config +from tradingagents.dataflows.interface import is_tool_configured def create_social_media_analyst(llm): @@ -10,13 +10,22 @@ def create_social_media_analyst(llm): current_date = state["trade_date"] instrument_context = build_instrument_context(state["company_of_interest"]) - tools = [ - get_social_sentiment, - get_news, - ] + social_tool_enabled = is_tool_configured("get_social_sentiment") + tools = [get_news] + social_tool_guidance = ( + "Use the get_news(query, start_date, end_date) tool to search for company-specific news and public discussion context." + ) + if social_tool_enabled: + tools.insert(0, get_social_sentiment) + social_tool_guidance = ( + "Use the get_social_sentiment(ticker, curr_date, look_back_days) tool for structured multi-source sentiment data when it is available, " + "and use the get_news(query, start_date, end_date) tool to search for company-specific news and public discussion context." + ) system_message = ( - "You are a social media and company specific news researcher/analyst tasked with analyzing social media posts, recent company news, and public sentiment for a specific company over the past week. You will be given a company's name your objective is to write a comprehensive long report detailing your analysis, insights, and implications for traders and investors on this company's current state after looking at social media and what people are saying about that company, analyzing sentiment data of what people feel each day about the company, and looking at recent company news. Use the get_social_sentiment(ticker, curr_date, look_back_days) tool for structured multi-source sentiment data when it is available, and use the get_news(query, start_date, end_date) tool to search for company-specific news and public discussion context. Try to look at all sources possible from social media to sentiment to news. Provide specific, actionable insights with supporting evidence to help traders make informed decisions." + "You are a social media and company specific news researcher/analyst tasked with analyzing social media posts, recent company news, and public sentiment for a specific company over the past week. You will be given a company's name your objective is to write a comprehensive long report detailing your analysis, insights, and implications for traders and investors on this company's current state after looking at social media and what people are saying about that company, analyzing sentiment data of what people feel each day about the company, and looking at recent company news. " + + social_tool_guidance + + " Try to look at all sources possible from social media to sentiment to news. Provide specific, actionable insights with supporting evidence to help traders make informed decisions." + """ Make sure to append a Markdown table at the end of the report to organize key points in the report, organized and easy to read.""" ) diff --git a/tradingagents/dataflows/interface.py b/tradingagents/dataflows/interface.py index 4540931d..4d013224 100644 --- a/tradingagents/dataflows/interface.py +++ b/tradingagents/dataflows/interface.py @@ -143,10 +143,25 @@ def get_vendor(category: str, method: str = None) -> str: # Fall back to category-level configuration return config.get("data_vendors", {}).get(category, "default") +def is_tool_configured(method: str) -> bool: + """Return True when a tool has an explicit non-default vendor configured.""" + category = get_category_for_method(method) + vendor_config = get_vendor(category, method) + configured_vendors = [ + vendor.strip().lower() + for vendor in str(vendor_config or "").split(",") + if vendor.strip() + ] + return any(vendor not in {"default", "none", "disabled"} for vendor in configured_vendors) + def route_to_vendor(method: str, *args, **kwargs): """Route method calls to appropriate vendor implementation with fallback support.""" category = get_category_for_method(method) vendor_config = get_vendor(category, method) + + if method == "get_social_sentiment" and not is_tool_configured(method): + raise RuntimeError("No configured vendor for 'get_social_sentiment'") + primary_vendors = [v.strip() for v in vendor_config.split(',')] if method not in VENDOR_METHODS: diff --git a/tradingagents/default_config.py b/tradingagents/default_config.py index fbedbe1d..b6b3b475 100644 --- a/tradingagents/default_config.py +++ b/tradingagents/default_config.py @@ -27,7 +27,7 @@ DEFAULT_CONFIG = { "technical_indicators": "yfinance", # Options: alpha_vantage, yfinance "fundamental_data": "yfinance", # Options: alpha_vantage, yfinance "news_data": "yfinance", # Options: alpha_vantage, yfinance - "social_data": "default", # Options: default, adanos + "social_data": "default", # Options: default (disabled), adanos }, # Tool-level configuration (takes precedence over category-level) "tool_vendors": { diff --git a/tradingagents/graph/trading_graph.py b/tradingagents/graph/trading_graph.py index afc4f19c..63989bdd 100644 --- a/tradingagents/graph/trading_graph.py +++ b/tradingagents/graph/trading_graph.py @@ -19,6 +19,7 @@ from tradingagents.agents.utils.agent_states import ( RiskDebateState, ) from tradingagents.dataflows.config import set_config +from tradingagents.dataflows.interface import is_tool_configured # Import the new abstract tool methods from agent_utils from tradingagents.agents.utils.agent_utils import ( @@ -158,6 +159,10 @@ class TradingAgentsGraph: def _create_tool_nodes(self) -> Dict[str, ToolNode]: """Create tool nodes for different data sources using abstract methods.""" + social_tools = [get_news] + if is_tool_configured("get_social_sentiment"): + social_tools.insert(0, get_social_sentiment) + return { "market": ToolNode( [ @@ -167,13 +172,7 @@ class TradingAgentsGraph: get_indicators, ] ), - "social": ToolNode( - [ - # Dedicated social sentiment first, then news context - get_social_sentiment, - get_news, - ] - ), + "social": ToolNode(social_tools), "news": ToolNode( [ # News and insider information